Skip to content

Commit 5b408c1

Browse files
committed
[lmdb facebookincubator#7] Optionally mount restored DBs using squashfs
1 parent 3e6ca88 commit 5b408c1

8 files changed

Lines changed: 80 additions & 37 deletions

File tree

glean/config/server/server_config.thrift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,12 @@ struct Config {
324324
// Args to pass to mksquashfs when creating a backup of an LMDB
325325
39: list<string> db_lmdb_mksquashfs_args =
326326
[ "-comp", "zstd", "-Xcompression-level", "8" ];
327+
328+
// LMDB: unpack a restored DB. If false, the DB will be mounted using
329+
// squashfs instead, which is more space-efficient but incurs a small
330+
// runtime cost to decompress the DB on demand. Mounting requires
331+
// squashfs-tools and squashfuse to be installed on Linux.
332+
40: bool db_lmdb_restore_unpack = true;
327333
}
328334

329335
// The following were automatically generated and may benefit from renaming.

glean/db/Glean/Database/Backup.hs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -310,13 +310,13 @@ doRestore env@Env{..} repo meta
310310
, Just (_, Some site, r_repo) <- fromRepoLocator envBackupBackends loc
311311
, r_repo == repo =
312312
loggingAction (runLogRepo "restore" env repo) (const mempty) $ do
313-
ServerConfig.Config{..} <- Observed.get envServerConfig
313+
cfg@ServerConfig.Config{..} <- Observed.get envServerConfig
314314
let maybeTimeout =
315315
case config_restore_timeout of
316316
Just seconds -> void . timeout (fromIntegral seconds * 1000000)
317317
Nothing -> id
318318
withStorageFor env repo meta $ \storage ->
319-
maybeTimeout $ restore site storage size `catch` handler storage
319+
maybeTimeout $ restore site storage cfg size `catch` handler storage
320320

321321
-- NOTE: No point in adding the repo to the sinbin if there was
322322
-- an exception, the handler removed it from the list of known DBs
@@ -333,8 +333,15 @@ doRestore env@Env{..} repo meta
333333
where
334334
say log s = log $ inRepo repo $ "restore: " ++ s
335335

336-
restore :: (Storage st, Site s) => s -> st -> Maybe Int64 -> IO ()
337-
restore site storage bytes = traceMsg envTracer (GleanTraceDownload repo) $ do
336+
restore
337+
:: (Storage st, Site s)
338+
=> s
339+
-> st
340+
-> ServerConfig.Config
341+
-> Maybe Int64
342+
-> IO ()
343+
restore site storage cfg bytes =
344+
traceMsg envTracer (GleanTraceDownload repo) $ do
338345
atomically $ notify envListener $ RestoreStarted repo
339346
mbFreeBytes <- (Just <$> Storage.getFreeCapacity storage)
340347
`catch` \(_ :: IOException) -> return Nothing
@@ -359,7 +366,7 @@ doRestore env@Env{..} repo meta
359366
say logInfo "restoring"
360367
createDirectoryIfMissing True scratch_restore
361368
traceMsg envTracer GleanTraceStorageRestore $
362-
Storage.restore storage repo scratch_restore scratch_file
369+
Storage.restore storage cfg repo scratch_restore scratch_file
363370
say logInfo "adding"
364371
traceMsg envTracer GleanTraceFinishRestore $
365372
Catalog.finishRestoring envCatalog repo

glean/db/Glean/Database/Storage.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ class DatabaseOps (Database s) => Storage s where
121121
-- to reduce the number of copies of the DB on disk during a restore.
122122
restore
123123
:: s -- ^ storage
124+
-> ServerConfig.Config -- ^ server config
124125
-> Repo -- ^ repo
125126
-> FilePath -- ^ scratch directory
126127
-> FilePath -- ^ file containing the serialiased database (produced by 'backup')

glean/db/Glean/Database/Storage/LMDB.hs

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module Glean.Database.Storage.LMDB
1212
) where
1313

1414
import Control.Exception
15+
import Control.Monad
1516
import Data.Int
1617
import qualified Data.Text as Text
1718
import Foreign.C.String
@@ -60,15 +61,25 @@ newStorage root ServerConfig.Config{..} = do
6061
Nothing -> Nothing
6162
}
6263

63-
newtype instance Database LMDB = Database DB
64-
deriving (CanLookup)
64+
data instance Database LMDB = Database DB LMDB
65+
66+
instance CanLookup (Database LMDB) where
67+
withLookup (Database db _) = withLookup db
68+
lookupName (Database db _) = lookupName db
6569

6670
instance Storage LMDB where
6771
describe db = "lmdb:" <> lmdbRoot db
6872

6973
open lmdb repo mode (DBVersion version) = do
7074
(cmode, start, ownership) <- case mode of
71-
ReadOnly -> return (0, invalidFid, Nothing)
75+
ReadOnly -> do
76+
exists <- doesDirectoryExist (path </> "data.mdb")
77+
when (not exists) $ do
78+
haveSquash <- doesFileExist squash
79+
when haveSquash $ do
80+
createDirectoryIfMissing True path
81+
callProcess "squashfuse_ll" [squash, path]
82+
return (0, invalidFid, Nothing)
7283
ReadWrite -> return (1, invalidFid, Nothing)
7384
Create start ownership _ -> do
7485
createDirectoryIfMissing True path
@@ -82,9 +93,10 @@ instance Storage LMDB where
8293
glean_lmdb_container_open_database container start
8394
first_unit_id version
8495
newForeignPtr glean_rocksdb_database_free p
85-
return (Database (DB (castForeignPtr fp) repo))
96+
return (Database (DB (castForeignPtr fp) repo) lmdb)
8697
where
8798
path = containerPath lmdb repo
99+
squash = path <.> "squashfs"
88100

89101
delete lmdb = safeRemovePathForcibly . containerPath lmdb
90102

@@ -116,39 +128,49 @@ instance Storage LMDB where
116128

117129
withScratchRoot rocks f = f $ lmdbRoot rocks </> ".scratch"
118130

119-
restore lmdb repo scratch scratch_file = do
131+
restore lmdb cfg repo scratch scratch_file = do
120132
withTempDirectory scratch "restore" $ \scratch_restore -> do
121-
let db = scratch_restore </> "db"
122-
createDirectoryIfMissing True db
123-
callProcess "unsquashfs" ["-d", db, scratch_file ]
124-
-- to avoid retaining an extra copy of the DB during restore,
125-
-- delete the input file now.
126133
let target = containerPath lmdb repo
127134
createDirectoryIfMissing True $ takeDirectory target
128-
renameDirectory db target
135+
if ServerConfig.config_db_lmdb_restore_unpack cfg
136+
then do
137+
let db = scratch_restore </> "db"
138+
createDirectoryIfMissing True db
139+
callProcess "unsquashfs" ["-d", db, scratch_file ]
140+
-- to avoid retaining an extra copy of the DB during restore,
141+
-- delete the input file now.
142+
renameDirectory db target
143+
else
144+
renameFile scratch_file (target <.> "squashfs")
129145

130146
containerPath :: LMDB -> Repo -> FilePath
131147
containerPath LMDB{..} repo = databasePath lmdbRoot repo </> "db"
132148

133149
instance DatabaseOps (Database LMDB) where
134-
close (Database db) = close db
135-
predicateStats (Database db) = predicateStats db
136-
store (Database db) = store db
137-
retrieve (Database db) = retrieve db
138-
commit (Database db) = commit db
139-
addOwnership (Database db) = addOwnership db
140-
optimize (Database db) = optimize db
141-
computeOwnership (Database db) = computeOwnership db
142-
storeOwnership (Database db) = storeOwnership db
143-
getOwnership (Database db) = getOwnership db
144-
getUnitId (Database db) = getUnitId db
145-
getUnit (Database db) = getUnit db
146-
addDefineOwnership (Database db) = addDefineOwnership db
147-
computeDerivedOwnership (Database db) = computeDerivedOwnership db
148-
cacheOwnership (Database db) = cacheOwnership db
149-
prepareFactOwnerCache (Database db) = prepareFactOwnerCache db
150-
151-
backup (Database db) cfg scratch process =
150+
close (Database db@(DB _ repo) lmdb) = do
151+
close db
152+
let path = containerPath lmdb repo; squash = path <.> "squashfs"
153+
haveSquash <- doesFileExist squash
154+
when haveSquash $ callProcess "umount" [path]
155+
`catch` \(_ :: IOException) -> return ()
156+
157+
predicateStats (Database db _) = predicateStats db
158+
store (Database db _) = store db
159+
retrieve (Database db _) = retrieve db
160+
commit (Database db _) = commit db
161+
addOwnership (Database db _) = addOwnership db
162+
optimize (Database db _) = optimize db
163+
computeOwnership (Database db _) = computeOwnership db
164+
storeOwnership (Database db _) = storeOwnership db
165+
getOwnership (Database db _) = getOwnership db
166+
getUnitId (Database db _) = getUnitId db
167+
getUnit (Database db _) = getUnit db
168+
addDefineOwnership (Database db _) = addDefineOwnership db
169+
computeDerivedOwnership (Database db _) = computeDerivedOwnership db
170+
cacheOwnership (Database db _) = cacheOwnership db
171+
prepareFactOwnerCache (Database db _) = prepareFactOwnerCache db
172+
173+
backup (Database db _) cfg scratch process =
152174
backup db cfg scratch $ \path _ -> do
153175
withTempDirectory scratch "out" $ \tmpdir -> do
154176
let out = tmpdir </> "db.squashfs"

glean/db/Glean/Database/Storage/Memory.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ instance Storage Memory where
7979
withScratchRoot _ f = withSystemTempDirectory "glean" f
8080

8181
-- TODO
82-
restore _ repo _ _ = dbError repo "unimplemented 'restore'"
82+
restore _ _ repo _ _ = dbError repo "unimplemented 'restore'"
8383

8484
instance CanLookup (Database Memory) where
8585
lookupName Database{..} = "memory:" <> repoToText dbRepo

glean/db/Glean/Database/Storage/RocksDB.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ instance Storage RocksDB where
162162

163163
withScratchRoot rocks f = f $ rocksRoot rocks </> ".scratch"
164164

165-
restore rocks repo scratch scratch_file =
165+
restore rocks _ repo scratch scratch_file =
166166
withTempDirectory scratch "restore" $ \scratch_restore -> do
167167
unTar scratch_file scratch_restore
168168
-- to avoid retaining an extra copy of the DB during restore,

glean/test/lib/Glean/Database/Test.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ module Glean.Database.Test
2020
, setDBVersion
2121
, setCompactOnCompletion
2222
, setMaxSetSize
23+
, setLMDBNoUnpack
2324
, enableTcDebug
2425
, enableQueryDebug
2526
, enableRocksDBCache
@@ -113,6 +114,11 @@ setMaxSetSize i cfg = cfg
113114
{ cfgServerConfig = cfgServerConfig cfg <&> \scfg -> scfg
114115
{ ServerConfig.config_max_set_size_bytes = Just i } }
115116

117+
setLMDBNoUnpack :: Setting
118+
setLMDBNoUnpack cfg = cfg
119+
{ cfgServerConfig = cfgServerConfig cfg <&> \scfg -> scfg
120+
{ ServerConfig.config_db_lmdb_restore_unpack = False } }
121+
116122
enableTcDebug :: Setting
117123
enableTcDebug cfg = cfg
118124
{ cfgDebug = (cfgDebug cfg) { tcDebug = True } }

glean/test/tests/BackupTest.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,8 @@ backends :: ([Setting] -> Test) -> Test
312312
backends fn =
313313
TestList [
314314
TestLabel lbl (fn settings)
315-
| (lbl, settings) <- allStorage
315+
| (lbl, settings) <- allStorage <>
316+
[("lmdb.squashfs", [setLMDBStorage, setLMDBNoUnpack])]
316317
, lbl /= "memory" -- doesn't support ownership yet
317318
]
318319

0 commit comments

Comments
 (0)