@@ -25,6 +25,7 @@ import qualified Data.ByteString.Char8 as SBS8
25
25
import qualified Data.ByteString.Lazy as LBS
26
26
import qualified Data.ByteString.Lazy.Char8 as LBS8
27
27
import Data.Foldable
28
+ import Data.Maybe (isJust )
28
29
import Data.Text (Text )
29
30
import qualified Data.Text as Text
30
31
import Data.Traversable
@@ -43,12 +44,25 @@ import qualified Store
43
44
import Config (StorageBackend (.. ))
44
45
45
46
data PersistentValue = PersistentValue
46
- { pvConfig :: PersistenceConfig
47
- , pvValue :: TVar Store. Value
47
+ { pvConfig :: PersistenceConfig
48
+ , pvValue :: TVar Store. Value
48
49
-- ^ contains the state of the whole database
49
- , pvIsDirty :: TVar Bool
50
+ , pvIsDirty :: TVar Bool
50
51
-- ^ 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.
52
66
}
53
67
54
68
data PersistenceConfig = PersistenceConfig
@@ -59,6 +73,14 @@ data PersistenceConfig = PersistenceConfig
59
73
, pcLogSync :: Bool
60
74
}
61
75
76
+ emptyJournalStatistics :: JournalStatistics
77
+ emptyJournalStatistics = JournalStatistics
78
+ { journalCount = 0
79
+ , journalTimeTotalMicros = 0
80
+ , journalTimeMaxMicros = 0
81
+ , journalTimeMinMicros = maxBound
82
+ }
83
+
62
84
-- | Get the actual value
63
85
getValue :: PersistentValue -> STM Store. Value
64
86
getValue = readTVar . pvValue
@@ -68,8 +90,19 @@ apply :: Store.Modification -> PersistentValue -> IO ()
68
90
apply op val = do
69
91
-- append to journal if enabled
70
92
for_ (pvJournal val) $ \ journalHandle -> do
93
+ start <- Clock. getTime Clock. Monotonic
71
94
let entry = Aeson. encode op
72
95
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
+ }
73
106
for_ (pcMetrics . pvConfig $ val) $ \ metrics -> do
74
107
journalPos <- hTell journalHandle
75
108
_ <- Metrics. incrementJournalWritten (LBS8. length entry) metrics
@@ -143,28 +176,45 @@ loadFromBackend backend config = runExceptT $ do
143
176
File -> readData dataFilePath
144
177
Sqlite -> readSqliteData dataFilePath
145
178
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
149
183
150
184
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
155
190
}
156
191
recoverJournal val
157
192
return val
158
193
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
+
159
209
syncToBackend :: StorageBackend -> PersistentValue -> IO ()
160
210
syncToBackend File pv = do
161
211
start <- Clock. getTime Clock. Monotonic
162
212
syncFile pv
163
213
end <- Clock. getTime Clock. Monotonic
164
214
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
168
218
syncToBackend Sqlite pv = syncSqliteFile pv
169
219
170
220
-- * SQLite loading and syncing
0 commit comments