Skip to content

Commit 6607470

Browse files
committed
[lmdb facebookincubator#2] Add support for multiple storage backends
1 parent f689e13 commit 6607470

22 files changed

Lines changed: 289 additions & 188 deletions

File tree

glean/db/Glean/Database/Backup.hs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import Glean.Database.Open
6060
import Glean.Database.Trace
6161
import Glean.Database.Types
6262
import Glean.Database.Schema
63+
import Glean.Database.Storage
6364
import Glean.Logger
6465
import Glean.RTS.Foreign.Ownership (getOwnershipStats, showOwnershipStats)
6566
import Glean.ServerConfig.Types (DatabaseBackupPolicy(..))
@@ -241,16 +242,17 @@ doBackup env@Env{..} repo prefix site =
241242
hasExcludeProperty repo (metaProperties meta) config_retention
242243
atomically $ notify envListener $ BackupStarted repo
243244
say logInfo "starting"
244-
withOpenDatabaseStorage env repo $ \_storage OpenDB{..} -> do
245+
withOpenDatabase env repo $ \OpenDB{..} -> do
245246
say logInfo "packing"
246247
stats <- mapMaybe
247248
(\(pid,stats) -> (,stats) . predicateRef <$> lookupPid pid odbSchema)
248249
<$> Storage.predicateStats odbHandle
249250
ownershipStats <- do
250251
maybeOwnership <- readTVarIO odbOwnership
251252
mapM getOwnershipStats maybeOwnership
253+
withStorageFor env repo meta $ \storage -> do
252254

253-
Backend.Data{..} <- withScratchDirectory envStorage repo $ \scratch ->
255+
Backend.Data{..} <- withScratchDirectory storage repo $ \scratch ->
254256
Storage.backup odbHandle scratch $ \path Data{dataSize} -> do
255257
say logInfo "uploading"
256258
let policy = ServerConfig.databaseBackupPolicy_repos config_backup
@@ -313,7 +315,8 @@ doRestore env@Env{..} repo meta
313315
case config_restore_timeout of
314316
Just seconds -> void . timeout (fromIntegral seconds * 1000000)
315317
Nothing -> id
316-
maybeTimeout $ restore site size `catch` handler
318+
withStorageFor env repo meta $ \storage ->
319+
maybeTimeout $ restore site storage size `catch` handler storage
317320

318321
-- NOTE: No point in adding the repo to the sinbin if there was
319322
-- an exception, the handler removed it from the list of known DBs
@@ -330,10 +333,10 @@ doRestore env@Env{..} repo meta
330333
where
331334
say log s = log $ inRepo repo $ "restore: " ++ s
332335

333-
restore :: Site s => s -> Maybe Int64 -> IO ()
334-
restore site bytes = traceMsg envTracer (GleanTraceDownload repo) $ do
336+
restore :: (Storage st, Site s) => s -> st -> Maybe Int64 -> IO ()
337+
restore site storage bytes = traceMsg envTracer (GleanTraceDownload repo) $ do
335338
atomically $ notify envListener $ RestoreStarted repo
336-
mbFreeBytes <- (Just <$> Storage.getFreeCapacity envStorage)
339+
mbFreeBytes <- (Just <$> Storage.getFreeCapacity storage)
337340
`catch` \(_ :: IOException) -> return Nothing
338341
case (mbFreeBytes, bytes) of
339342
(Just freeBytes, Just size) -> do
@@ -345,7 +348,7 @@ doRestore env@Env{..} repo meta
345348
neededBytes freeBytes
346349
_ -> return ()
347350

348-
withScratchDirectory envStorage repo $ \scratch -> do
351+
withScratchDirectory storage repo $ \scratch -> do
349352
say logInfo "starting"
350353
say logInfo "downloading"
351354
let scratch_restore = scratch </> "restore"
@@ -356,14 +359,15 @@ doRestore env@Env{..} repo meta
356359
say logInfo "restoring"
357360
createDirectoryIfMissing True scratch_restore
358361
traceMsg envTracer GleanTraceStorageRestore $
359-
Storage.restore envStorage repo scratch_restore scratch_file
362+
Storage.restore storage repo scratch_restore scratch_file
360363
say logInfo "adding"
361364
traceMsg envTracer GleanTraceFinishRestore $
362365
Catalog.finishRestoring envCatalog repo
363366
atomically $ notify envListener $ RestoreFinished repo
364367
say logInfo "finished"
365368

366-
handler exc = do
369+
handler :: Storage s => s -> SomeException -> IO ()
370+
handler storage exc = do
367371
failed <- atomically $ do
368372
failed <- Catalog.exists envCatalog [Restoring] repo
369373
when failed $ do
@@ -372,7 +376,7 @@ doRestore env@Env{..} repo meta
372376
return failed
373377
when failed $ do
374378
say logError $ "failed: " ++ show exc
375-
swallow $ Storage.safeRemoveForcibly envStorage repo
379+
swallow $ Storage.safeRemoveForcibly storage repo
376380
rethrowAsync exc
377381

378382

glean/db/Glean/Database/Close.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ closeDatabases env@Env{..} = do
3939
dbs <- readTVarIO envActive
4040
forM_ (HashMap.keys dbs) $ closeDatabase env
4141

42-
isIdle :: (TimePoint -> Bool) -> DB s -> OpenDB s -> STM Bool
42+
isIdle :: (TimePoint -> Bool) -> DB -> OpenDB -> STM Bool
4343
isIdle long_enough db odb = and <$> sequence
4444
[ (== 1) <$> readTVar (dbUsers db) -- we are the only user
4545
, long_enough <$> readTVar (odbIdleSince odb)
@@ -52,7 +52,7 @@ isIdle long_enough db odb = and <$> sequence
5252
]
5353

5454
closeIf
55-
:: (forall s . DB s -> DBState s -> STM (Maybe (OpenDB s)))
55+
:: (DB -> DBState -> STM (Maybe OpenDB))
5656
-> Env
5757
-> Repo
5858
-> IO ()
@@ -132,7 +132,7 @@ exportOpenDBStats Env{..} = do
132132
forM_ (HashMap.toList repoOpenCounts) $ \(repoNm,count) -> do
133133
setCounter ("glean.db." <> Text.encodeUtf8 repoNm <> ".open") count
134134

135-
closeOpenDB :: Storage.Storage s => Env -> OpenDB s -> IO ()
135+
closeOpenDB :: Env -> OpenDB -> IO ()
136136
closeOpenDB env OpenDB{..} = do
137137
case odbWriting of
138138
Just Writing{..} -> do

glean/db/Glean/Database/Config.hs

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
module Glean.Database.Config (
1111
-- * DataStore
1212
DataStore(..),
13+
StorageName,
1314
fileDataStore,
1415
tmpDataStore,
1516
memoryDataStore,
17+
rocksdbName,
1618

1719
-- * Config, and options parser
1820
options,
@@ -84,6 +86,7 @@ import qualified Glean.Database.Storage.Memory as Memory
8486
import qualified Glean.Database.Storage.RocksDB as RocksDB
8587
import Glean.Database.Trace
8688
import qualified Glean.Internal.Types as Internal
89+
import Glean.Internal.Types (StorageName(..))
8790
import Glean.DefaultConfigs
8891
import Glean.Logger.Database
8992
import Glean.Logger.Server
@@ -104,36 +107,51 @@ import Paths_glean
104107
#endif
105108

106109
data DataStore = DataStore
107-
{ withDataStore
108-
:: forall a. ServerConfig.Config
109-
-> (forall c s. (Catalog.Store c, Storage s) => c -> s -> IO a)
110-
-> IO a
110+
{ withStorage ::
111+
-- setup the storage backend. Must be scoped, because it might
112+
-- involve creating temporary resources.
113+
forall a.
114+
ServerConfig.Config ->
115+
((HashMap StorageName (Some Storage), Some Catalog.Store) -> IO a) ->
116+
IO a
117+
, defaultStorage :: StorageName
111118
, dataStoreTag :: String
112119
}
113120

121+
rocksdbName, memoryName :: StorageName
122+
rocksdbName = StorageName "rocksdb"
123+
memoryName = StorageName "memory"
124+
114125
fileDataStore :: FilePath -> DataStore
115126
fileDataStore path = DataStore
116-
{ withDataStore = \scfg f -> do
127+
{ withStorage = \scfg f -> do
117128
rocksdb <- RocksDB.newStorage path scfg
118-
f (Catalog.fileCatalog path) rocksdb
119-
, dataStoreTag = "rocksdb:" <> path
129+
f (
130+
HashMap.fromList
131+
[ (rocksdbName, Some rocksdb)
132+
],
133+
Some (Catalog.fileCatalog path)
134+
)
135+
, defaultStorage = rocksdbName
136+
, dataStoreTag = "db:" <> path
120137
}
121138

122139
tmpDataStore :: DataStore
123140
tmpDataStore = DataStore
124-
{ withDataStore = \scfg f -> withSystemTempDirectory "glean" $ \tmp -> do
141+
{ withStorage = \scfg f -> withSystemTempDirectory "glean" $ \tmp -> do
125142
logInfo $ "Storing temporary DBs in " <> tmp
126-
rocksdb <- RocksDB.newStorage tmp scfg
127-
f (Catalog.fileCatalog tmp) rocksdb
128-
, dataStoreTag = "rocksdb:{TMP}"
143+
withStorage (fileDataStore tmp) scfg f
144+
, defaultStorage = rocksdbName
145+
, dataStoreTag = dataStoreTag (fileDataStore "<tmp>")
129146
}
130147

131148
memoryDataStore :: DataStore
132149
memoryDataStore = DataStore
133-
{ withDataStore = \_ f -> do
150+
{ withStorage = \_ f -> do
134151
cat <- Catalog.memoryCatalog
135152
mem <- Memory.newStorage
136-
f cat mem
153+
f (HashMap.fromList [(memoryName, Some mem)], Some cat)
154+
, defaultStorage = memoryName
137155
, dataStoreTag = "memory"
138156
}
139157

@@ -488,10 +506,12 @@ schemaLocationOption = option (eitherReader schemaLocationParser)
488506
options :: Parser Config
489507
options = do
490508
let
491-
dbRoot = fileDataStore <$> strOption (
492-
long "db-root" <>
493-
metavar "DIR" <>
494-
help "Directory containing databases")
509+
dbRoot = do
510+
path <- strOption (
511+
long "db-root" <>
512+
metavar "DIR" <>
513+
help "Directory containing databases")
514+
pure $ fileDataStore path
495515
dbTmp = tmpDataStore <$ flag' () (
496516
long "db-tmp" <>
497517
help "Store databases in a temporary directory (default)")

glean/db/Glean/Database/Create.hs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -115,29 +115,28 @@ kickOffDatabase env@Env{..} kickOff@Thrift.KickOff{..}
115115
handle
116116
(\Catalog.EntryAlreadyExists{} ->
117117
return $ Thrift.KickOffResponse True) $
118-
mask $ \unmask ->
118+
mask $ \unmask -> do
119119
-- FIXME: There is a tiny race here where we might fail in a weird way
120120
-- if kick off a DB that is being deleted after it got removed from
121121
-- the Catalog but before it got removed from the storage. The entire
122122
-- concept of deleting DBs will change with the new metadata handling so
123123
-- it's not worth fixing at this point, especially since we aren't
124124
-- supposed to be kicking off DBs we've previously deleted.
125+
let meta = newMeta envDefaultStorage version time
126+
(Incomplete def) allProps
127+
(lightDeps kickOff_dependencies')
125128
bracket_
126-
(Catalog.create
127-
envCatalog
128-
kickOff_repo
129-
(newMeta version time (Incomplete def) allProps
130-
(lightDeps kickOff_dependencies')) $ do
131-
modifyTVar' envActive $ HashMap.insert kickOff_repo db
132-
writeTVar (dbState db) Opening
133-
acquireDB db)
129+
(Catalog.create envCatalog kickOff_repo meta $ do
130+
modifyTVar' envActive $ HashMap.insert kickOff_repo db
131+
writeTVar (dbState db) Opening
132+
acquireDB db)
134133
(atomically $ releaseDB envCatalog envActive db) $
135-
do
134+
withStorageFor env kickOff_repo meta $ \storage -> do
136135
-- Open the new db in Create mode which will create the
137136
-- physical storage. This might fail - in that case, we
138137
-- mark the db as failed. NB. pass the full dependencies
139138
-- here, not lightDeps.
140-
opener <- asyncOpenDB env envStorage db version mode
139+
opener <- asyncOpenDB env storage db version mode
141140
kickOff_dependencies'
142141
(do
143142
logInfo $ inRepo kickOff_repo "created")

glean/db/Glean/Database/Data.hs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import Data.ByteString.Lazy (toStrict, fromStrict)
2222
import Thrift.Protocol.Compact
2323

2424
import Glean.Database.Exception
25-
import Glean.Database.Storage (Storage, Database)
25+
import Glean.Database.Storage (DatabaseOps)
2626
import qualified Glean.Database.Storage as Storage
2727
import Glean.RTS.Foreign.Ownership
2828
import Glean.Types (Repo)
@@ -39,21 +39,21 @@ uNITS_KEY = "units"
3939
sLICES_KEY :: ByteString
4040
sLICES_KEY = "slices"
4141

42-
storeSchema :: Storage s => Database s -> StoredSchema -> IO ()
42+
storeSchema :: DatabaseOps db => db -> StoredSchema -> IO ()
4343
storeSchema db = Storage.store db sCHEMA_KEY . serializeCompact
4444

45-
retrieveSchema :: Storage s => Repo -> Database s -> IO (Maybe StoredSchema)
45+
retrieveSchema :: DatabaseOps db => Repo -> db -> IO (Maybe StoredSchema)
4646
retrieveSchema repo db = do
4747
value <- Storage.retrieve db sCHEMA_KEY
4848
case deserializeCompact <$> value of
4949
Just (Right info) -> return $ Just info
5050
Just (Left msg) -> dbError repo $ "invalid schema: " ++ msg
5151
Nothing -> return Nothing
5252

53-
storeUnits :: Storage s => Database s -> [ByteString] -> IO ()
53+
storeUnits :: DatabaseOps db => db -> [ByteString] -> IO ()
5454
storeUnits db = Storage.store db uNITS_KEY . toStrict . encode
5555

56-
retrieveUnits :: Storage s => Repo -> Database s -> IO (Maybe [ByteString])
56+
retrieveUnits :: DatabaseOps db => Repo -> db -> IO (Maybe [ByteString])
5757
retrieveUnits repo db = do
5858
value <- Storage.retrieve db uNITS_KEY
5959
case decodeOrFail . fromStrict <$> value of
@@ -64,12 +64,12 @@ retrieveUnits repo db = do
6464
-- Slices are each serialized using the RTS Slice::serialize(), and then
6565
-- the list of serialized slices :: [ByteString] is serialized with
6666
-- the Haskell Binary encoder.
67-
storeSlices :: Storage s => Database s -> [Slice] -> IO ()
67+
storeSlices :: DatabaseOps db => db -> [Slice] -> IO ()
6868
storeSlices db slices = do
6969
bytestrings <- mapM serializeSlice slices
7070
Storage.store db sLICES_KEY $ toStrict $ encode bytestrings
7171

72-
retrieveSlices :: Storage s => Repo -> Database s -> IO (Maybe [Slice])
72+
retrieveSlices :: DatabaseOps db => Repo -> db -> IO (Maybe [Slice])
7373
retrieveSlices repo db = do
7474
value <- Storage.retrieve db sLICES_KEY
7575
case decodeOrFail . fromStrict <$> value of

glean/db/Glean/Database/Delete.hs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,9 @@ expireDatabase delay env@Env{..} repo = do
6363

6464
-- | Database deletion thread
6565
removeDatabase
66-
:: Storage.Storage s
67-
=> Env
66+
:: Env
6867
-> Repo
69-
-> TMVar (Maybe (DB s))
68+
-> TMVar (Maybe DB)
7069
-> IO ()
7170
removeDatabase env@Env{..} repo todo = uninterruptibleMask_ $
7271
-- This runs under uninterruptibleMask_ because there is really nothing
@@ -94,12 +93,14 @@ removeDatabase env@Env{..} repo todo = uninterruptibleMask_ $
9493
Open odb -> closeOpenDB env odb
9594
`finally` atomically (writeTVar dbState Closed)
9695
_ -> return ()
97-
Storage.delete envStorage repo
98-
Catalog.delete envCatalog repo
99-
Storage.safeRemoveForcibly envStorage repo
96+
meta <- atomically $ Catalog.readMeta envCatalog repo
97+
withStorageFor env repo meta $ \storage -> do
98+
Storage.delete storage repo
99+
Catalog.delete envCatalog repo
100+
Storage.safeRemoveForcibly storage repo
100101
atomically $ modifyTVar envDerivations $
101102
HashMap.filterWithKey (\(repo',_) _ -> repo' /= repo)
102-
logInfo $ inRepo repo "deleted"
103+
logInfo $ inRepo repo "deleted"
103104

104105
-- | Schedule a DB for deletion and return the 'Async' which can be used to
105106
-- obtain the result.

0 commit comments

Comments
 (0)