From d4264e1e829a969d079b3658b20da7f6706f05df Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 4 May 2020 15:49:39 -0700 Subject: [PATCH] walletdb/bdb: attempt to set initial memory map size when opening In this commit, we start to set the initial memory map size when opening the database. This helps with bulk insertions or migrations of the database, as if we set this to a larger value, then bbolt needs to remap less often. Remapping can be memory intensive as it needs to copy over the entire existing database. For 32-bit systems, we take care to clamp this value to ensure we don't exceed the largest addressable memory on such systems. --- walletdb/bdb/db.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/walletdb/bdb/db.go b/walletdb/bdb/db.go index c96d14685d..430c963065 100644 --- a/walletdb/bdb/db.go +++ b/walletdb/bdb/db.go @@ -365,6 +365,18 @@ func fileExists(name string) bool { return true } +const ( + // is64Bit tells us if we're running on a 64-bit system or not. If uintptr is + // 32 bits, then its complement will be 0xffffffff rather than + // 0xffffffffffffffff. The complement of uint64(0) will always be + // 0xffffffffffffffff. + is64Bit = uint64(^uintptr(0)) == ^uint64(0) + + // max32BitMapSize is the largest mmap size we can support on 32-bit + // systems. (2^31)-1 == 0x7FFFFFFF. + max32BitMapSize = 0x7FFFFFFF +) + // openDB opens the database at the provided path. walletdb.ErrDbDoesNotExist // is returned if the database doesn't exist and the create flag is not set. func openDB(dbPath string, create bool, options *bbolt.Options) (walletdb.DB, error) { @@ -375,6 +387,30 @@ func openDB(dbPath string, create bool, options *bbolt.Options) (walletdb.DB, er // Specify bbolt freelist options to reduce heap pressure in case the // freelist grows to be very large. options.FreelistType = bbolt.FreelistMapType + + if !create { + // The other value that we want to set is the initial memory + // map size. When bolt expands the mmap, it actually copies + // over the entire database. By setting this to a large value + // than zero, then we can avoid some of that heap pressure due + // to the copies. + dbFileInfo, err := os.Stat(dbPath) + if err != nil { + return nil, err + } + + // We'll aim to set the initial memory map to 2x the size of + // the database as it exists on disk, meaning the DB size can + // double without us having to remap everything. + mmapSize := dbFileInfo.Size() * 2 + + // If we're on a 32-bit system, then we'll need to limit this + // value to ensure we don't run into errors down the line. + if !is64Bit { + mmapSize = max32BitMapSize + } + + options.InitialMmapSize = int(mmapSize) } boltDB, err := bbolt.Open(dbPath, 0600, options)