Do not use vacuum when pruning (#1103)
* Do not use vacuum when pruning
This commit is contained in:
parent
8b0d700b45
commit
af10e8f179
|
@ -49,6 +49,7 @@ type
|
||||||
kv: KvStoreRef
|
kv: KvStoreRef
|
||||||
maxSize: uint32
|
maxSize: uint32
|
||||||
sizeStmt: SqliteStmt[NoParams, int64]
|
sizeStmt: SqliteStmt[NoParams, int64]
|
||||||
|
unusedSizeStmt: SqliteStmt[NoParams, int64]
|
||||||
vacStmt: SqliteStmt[NoParams, void]
|
vacStmt: SqliteStmt[NoParams, void]
|
||||||
getAll: SqliteStmt[NoParams, RowInfo]
|
getAll: SqliteStmt[NoParams, RowInfo]
|
||||||
|
|
||||||
|
@ -85,6 +86,10 @@ proc new*(T: type ContentDB, path: string, maxSize: uint32, inMemory = false): C
|
||||||
"SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size();",
|
"SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size();",
|
||||||
NoParams, int64).get()
|
NoParams, int64).get()
|
||||||
|
|
||||||
|
let unusedSize = db.prepareStmt(
|
||||||
|
"SELECT freelist_count * page_size as size FROM pragma_freelist_count(), pragma_page_size();",
|
||||||
|
NoParams, int64).get()
|
||||||
|
|
||||||
let vacStmt = db.prepareStmt(
|
let vacStmt = db.prepareStmt(
|
||||||
"VACUUM;",
|
"VACUUM;",
|
||||||
NoParams, void).get()
|
NoParams, void).get()
|
||||||
|
@ -99,7 +104,13 @@ proc new*(T: type ContentDB, path: string, maxSize: uint32, inMemory = false): C
|
||||||
).get()
|
).get()
|
||||||
|
|
||||||
ContentDB(
|
ContentDB(
|
||||||
kv: kvStore, maxSize: maxSize, sizeStmt: getSizeStmt, vacStmt: vacStmt, getAll: getKeysStmt)
|
kv: kvStore,
|
||||||
|
maxSize: maxSize,
|
||||||
|
sizeStmt: getSizeStmt,
|
||||||
|
vacStmt: vacStmt,
|
||||||
|
getAll: getKeysStmt,
|
||||||
|
unusedSizeStmt: unusedSize
|
||||||
|
)
|
||||||
|
|
||||||
proc getNFurthestElements*(
|
proc getNFurthestElements*(
|
||||||
db: ContentDB, target: UInt256, n: uint64): (seq[ObjInfo], int64) =
|
db: ContentDB, target: UInt256, n: uint64): (seq[ObjInfo], int64) =
|
||||||
|
@ -172,6 +183,18 @@ proc size*(db: ContentDB): int64 =
|
||||||
size = res).expectDb()
|
size = res).expectDb()
|
||||||
return size
|
return size
|
||||||
|
|
||||||
|
proc unusedSize(db: ContentDB): int64 =
|
||||||
|
## Returns the total size of the pages which are unused by the database,
|
||||||
|
## i.e they can be re-used for new content.
|
||||||
|
|
||||||
|
var size: int64 = 0
|
||||||
|
discard (db.unusedSizeStmt.exec do(res: int64):
|
||||||
|
size = res).expectDb()
|
||||||
|
return size
|
||||||
|
|
||||||
|
proc realSize*(db: ContentDB): int64 =
|
||||||
|
db.size() - db.unusedSize()
|
||||||
|
|
||||||
proc get*(db: ContentDB, key: openArray[byte]): Option[seq[byte]] =
|
proc get*(db: ContentDB, key: openArray[byte]): Option[seq[byte]] =
|
||||||
var res: Option[seq[byte]]
|
var res: Option[seq[byte]]
|
||||||
proc onData(data: openArray[byte]) = res = some(@data)
|
proc onData(data: openArray[byte]) = res = some(@data)
|
||||||
|
@ -210,7 +233,7 @@ proc contains*(db: ContentDB, key: ContentId): bool =
|
||||||
proc del*(db: ContentDB, key: ContentId) =
|
proc del*(db: ContentDB, key: ContentId) =
|
||||||
db.del(key.toByteArrayBE())
|
db.del(key.toByteArrayBE())
|
||||||
|
|
||||||
proc deleteFractionOfContent(
|
proc deleteFractionOfContent*(
|
||||||
db: ContentDB,
|
db: ContentDB,
|
||||||
target: Uint256,
|
target: Uint256,
|
||||||
targetFraction: float64): (UInt256, int64, int64, int64) =
|
targetFraction: float64): (UInt256, int64, int64, int64) =
|
||||||
|
@ -254,7 +277,17 @@ proc put*(
|
||||||
|
|
||||||
db.put(key, value)
|
db.put(key, value)
|
||||||
|
|
||||||
let dbSize = db.size()
|
# We use real size for our pruning threshold, which means that database file
|
||||||
|
# will reach size specified in db.maxSize, and will stay that size thorough
|
||||||
|
# node life time, as after content deletion free pages will be re used.
|
||||||
|
# TODO:
|
||||||
|
# 1. Devise vacuum strategy - after few pruning cycles database can become
|
||||||
|
# fragmented which may impact performance, so at some point in time `VACUUM`
|
||||||
|
# will need to be run to defragment the db.
|
||||||
|
# 2. Deal with the edge case where a user configures max db size lower than
|
||||||
|
# current db.size(). With such config the database would try to prune itself with
|
||||||
|
# each addition.
|
||||||
|
let dbSize = db.realSize()
|
||||||
|
|
||||||
if dbSize < int64(db.maxSize):
|
if dbSize < int64(db.maxSize):
|
||||||
return PutResult(kind: ContentStored)
|
return PutResult(kind: ContentStored)
|
||||||
|
@ -270,8 +303,6 @@ proc put*(
|
||||||
|
|
||||||
let deletedFraction = float64(deletedBytes) / float64(totalContentSize)
|
let deletedFraction = float64(deletedBytes) / float64(totalContentSize)
|
||||||
|
|
||||||
db.reclaimSpace()
|
|
||||||
|
|
||||||
return PutResult(
|
return PutResult(
|
||||||
kind: DbPruned,
|
kind: DbPruned,
|
||||||
furthestStoredElementDistance: furthestNonDeletedElement,
|
furthestStoredElementDistance: furthestNonDeletedElement,
|
||||||
|
|
|
@ -72,27 +72,35 @@ suite "Content Database":
|
||||||
let size3 = db.size()
|
let size3 = db.size()
|
||||||
discard db.put(u256(2), genByteSeq(numBytes), testId)
|
discard db.put(u256(2), genByteSeq(numBytes), testId)
|
||||||
let size4 = db.size()
|
let size4 = db.size()
|
||||||
|
let realSize = db.realSize()
|
||||||
|
|
||||||
check:
|
check:
|
||||||
size2 > size1
|
size2 > size1
|
||||||
size3 > size2
|
size3 > size2
|
||||||
size3 == size4
|
size3 == size4
|
||||||
|
realSize == size4
|
||||||
|
|
||||||
db.del(u256(2))
|
db.del(u256(2))
|
||||||
db.del(u256(1))
|
db.del(u256(1))
|
||||||
|
|
||||||
|
let realSize1 = db.realSize()
|
||||||
let size5 = db.size()
|
let size5 = db.size()
|
||||||
|
|
||||||
check:
|
check:
|
||||||
size4 == size5
|
size4 == size5
|
||||||
|
# real size will be smaller as after del, there are free pages in sqlite
|
||||||
|
# which can be re-used for further additions
|
||||||
|
realSize1 < size5
|
||||||
|
|
||||||
db.reclaimSpace()
|
db.reclaimSpace()
|
||||||
|
|
||||||
let size6 = db.size()
|
let size6 = db.size()
|
||||||
|
let realSize2 = db.realSize()
|
||||||
|
|
||||||
check:
|
check:
|
||||||
# After space reclamation size of db should be equal to initial size
|
# After space reclamation size of db should be equal to initial size
|
||||||
size6 == size1
|
size6 == size1
|
||||||
|
realSize2 == size6
|
||||||
|
|
||||||
type TestCase = object
|
type TestCase = object
|
||||||
keys: seq[UInt256]
|
keys: seq[UInt256]
|
||||||
|
@ -183,7 +191,7 @@ suite "Content Database":
|
||||||
|
|
||||||
check:
|
check:
|
||||||
pr10.numOfDeletedElements == 2
|
pr10.numOfDeletedElements == 2
|
||||||
uint32(db.size()) < maxDbSize
|
uint32(db.realSize()) < maxDbSize
|
||||||
# With current settings 2 furthers elements will be delted i.e 30 and 40
|
# With current settings 2 furthers elements will be delted i.e 30 and 40
|
||||||
# so the furthest non deleted one will be 20
|
# so the furthest non deleted one will be 20
|
||||||
pr10.furthestStoredElementDistance == thirdFurthest
|
pr10.furthestStoredElementDistance == thirdFurthest
|
||||||
|
|
Loading…
Reference in New Issue