Skip to content

Commit 5c0a16c

Browse files
authored
Merge pull request #96 from channable/dd/journaling-log
Add logging on duration of journaling operations
2 parents 067f9c1 + a1a7685 commit 5c0a16c

File tree

1 file changed

+64
-14
lines changed

1 file changed

+64
-14
lines changed

server/src/Persistence.hs

+64-14
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import qualified Data.ByteString.Char8 as SBS8
2525
import qualified Data.ByteString.Lazy as LBS
2626
import qualified Data.ByteString.Lazy.Char8 as LBS8
2727
import Data.Foldable
28+
import Data.Maybe (isJust)
2829
import Data.Text (Text)
2930
import qualified Data.Text as Text
3031
import Data.Traversable
@@ -43,12 +44,25 @@ import qualified Store
4344
import Config (StorageBackend (..))
4445

4546
data PersistentValue = PersistentValue
46-
{ pvConfig :: PersistenceConfig
47-
, pvValue :: TVar Store.Value
47+
{ pvConfig :: PersistenceConfig
48+
, pvValue :: TVar Store.Value
4849
-- ^ contains the state of the whole database
49-
, pvIsDirty :: TVar Bool
50+
, pvIsDirty :: TVar Bool
5051
-- ^ flag indicating whether the current value of 'pvValue' has not yet been persisted to disk
51-
, pvJournal :: Maybe Handle
52+
, pvJournal :: Maybe Handle
53+
, pvJournalStats :: TVar JournalStatistics
54+
-- ^ Structure keeping track of the duration of journaling operations.
55+
}
56+
57+
data JournalStatistics = JournalStatistics
58+
{ journalCount :: !Int
59+
-- ^ The number of times a command has been journaled.
60+
, journalTimeTotalMicros :: !Int
61+
-- ^ The total (sum) time journaling took.
62+
, journalTimeMaxMicros :: !Int
63+
-- ^ The maximum time a journaling operation took.
64+
, journalTimeMinMicros :: !Int
65+
-- ^ The minimum time a journaling operation took.
5266
}
5367

5468
data PersistenceConfig = PersistenceConfig
@@ -59,6 +73,14 @@ data PersistenceConfig = PersistenceConfig
5973
, pcLogSync :: Bool
6074
}
6175

76+
emptyJournalStatistics :: JournalStatistics
77+
emptyJournalStatistics = JournalStatistics
78+
{ journalCount = 0
79+
, journalTimeTotalMicros = 0
80+
, journalTimeMaxMicros = 0
81+
, journalTimeMinMicros = maxBound
82+
}
83+
6284
-- | Get the actual value
6385
getValue :: PersistentValue -> STM Store.Value
6486
getValue = readTVar . pvValue
@@ -68,8 +90,19 @@ apply :: Store.Modification -> PersistentValue -> IO ()
6890
apply op val = do
6991
-- append to journal if enabled
7092
for_ (pvJournal val) $ \journalHandle -> do
93+
start <- Clock.getTime Clock.Monotonic
7194
let entry = Aeson.encode op
7295
LBS8.hPutStrLn journalHandle entry
96+
end <- Clock.getTime Clock.Monotonic
97+
let time = fromInteger $ Clock.toNanoSecs (Clock.diffTimeSpec end start) `div` 1000
98+
-- Update the structure containing the statistics on journaling.
99+
atomically $ do
100+
modifyTVar' (pvJournalStats val) $
101+
\s -> s { journalCount = journalCount s + 1
102+
, journalTimeTotalMicros = journalTimeTotalMicros s + time
103+
, journalTimeMaxMicros = max (journalTimeMaxMicros s) time
104+
, journalTimeMinMicros = min (journalTimeMinMicros s) time
105+
}
73106
for_ (pcMetrics . pvConfig $ val) $ \metrics -> do
74107
journalPos <- hTell journalHandle
75108
_ <- Metrics.incrementJournalWritten (LBS8.length entry) metrics
@@ -143,28 +176,45 @@ loadFromBackend backend config = runExceptT $ do
143176
File -> readData dataFilePath
144177
Sqlite -> readSqliteData dataFilePath
145178

146-
valueVar <- lift $ newTVarIO value
147-
dirtyVar <- lift $ newTVarIO False
148-
journal <- for (pcJournalFile config) openJournal
179+
valueVar <- lift $ newTVarIO value
180+
dirtyVar <- lift $ newTVarIO False
181+
journalStats <- lift $ newTVarIO emptyJournalStatistics
182+
journal <- for (pcJournalFile config) openJournal
149183

150184
let val = PersistentValue
151-
{ pvConfig = config
152-
, pvValue = valueVar
153-
, pvIsDirty = dirtyVar
154-
, pvJournal = journal
185+
{ pvConfig = config
186+
, pvValue = valueVar
187+
, pvIsDirty = dirtyVar
188+
, pvJournal = journal
189+
, pvJournalStats = journalStats
155190
}
156191
recoverJournal val
157192
return val
158193

194+
logJournalStats :: PersistentValue -> IO ()
195+
logJournalStats pv = do
196+
jStats <- atomically $ do
197+
stats <- readTVar (pvJournalStats pv)
198+
writeTVar (pvJournalStats pv) emptyJournalStatistics
199+
return stats
200+
let jCount = journalCount jStats
201+
when (jCount /= 0) $ do
202+
let jAvg = journalTimeTotalMicros jStats `div` jCount
203+
jMin = journalTimeMinMicros jStats
204+
jMax = journalTimeMaxMicros jStats
205+
jLog = Text.concat ["Journaling duration (avg/min/max/count) in microseconds since last Sync: "
206+
, Text.intercalate "/" $ map (Text.pack . show) [jAvg, jMin, jMax, jCount]]
207+
logMessage pv jLog
208+
159209
syncToBackend :: StorageBackend -> PersistentValue -> IO ()
160210
syncToBackend File pv = do
161211
start <- Clock.getTime Clock.Monotonic
162212
syncFile pv
163213
end <- Clock.getTime Clock.Monotonic
164214
let time = Clock.toNanoSecs (Clock.diffTimeSpec end start) `div` 1000000
165-
if pcLogSync $ pvConfig pv then
166-
logMessage pv $ Text.concat ["It took ", Text.pack $ show time, " ms to synchronize Icepeak on disk."]
167-
else return ()
215+
when (pcLogSync $ pvConfig pv) $ do
216+
logMessage pv $ Text.concat ["It took ", Text.pack $ show time, " ms to synchronize Icepeak on disk."]
217+
when (isJust $ pcJournalFile $ pvConfig pv) $ logJournalStats pv
168218
syncToBackend Sqlite pv = syncSqliteFile pv
169219

170220
-- * SQLite loading and syncing

0 commit comments

Comments
 (0)