module Snowflake
( Timestamp
, Conf(..)
, IdWorker(..)
, defaultConf
, next
, nexts
) where
import Prelude hiding (sequence)
import Data.Int (Int64)
import Data.Maybe (Maybe, isNothing, fromJust)
import Data.Bits ( (.|.)
, shiftL
)
import Data.Time.Exts.Unix ( getCurrentUnixDateTimeMillis
, UnixDateTimeMillis(..)
)
type Timestamp = Int64
data Conf = Conf { _sequenceBits :: Int
, _workerIdBits :: Int
, _datacenterIdBits :: Int
, _twepoch :: Timestamp
} deriving (Show)
data IdWorker = IdWorker { _sequence :: Int64
, _workerId :: Int64
, _datacenterId :: Int64
, _timestamp :: Timestamp
, _conf :: Conf
} deriving (Show)
defaultConf = Conf { _sequenceBits = 12
, _workerIdBits = 5
, _datacenterIdBits = 5
, _twepoch = 1472733628921
}
next :: IdWorker -> Timestamp -> (Maybe Int64, IdWorker)
next worker@(IdWorker lastSq wid did lastTs (Conf sBits wBits dBits twepoch)) ts
| lastTs == ts && lastSq == 2^sBits 1 = (Nothing, worker)
| otherwise = (Just newId, worker {_sequence = newSq, _timestamp = ts})
where
newSq = if lastTs == ts then lastSq + 1 else 0
newId = tBit .|. dBit .|. wBit .|. newSq
wBit = wid `shiftL` sBits
dBit = did `shiftL` (sBits + wBits)
tBit = (ts twepoch) `shiftL` (sBits + wBits + dBits)
nexts :: IdWorker -> Int -> IO ([Int64], IdWorker)
nexts worker 0 = return ([], worker)
nexts worker n = do
timestamp <- _udt_mil_base <$> getCurrentUnixDateTimeMillis
let (maybeNewId, newWorker) = next worker timestamp in
if isNothing maybeNewId
then nexts worker n
else (\(ids, wk) -> (fromJust maybeNewId : ids, wk)) <$> nexts newWorker (n1)