From a54168a2702a3cfa2a5c3dbb86c3eec2b296ebfe Mon Sep 17 00:00:00 2001 From: Xiliang Chen Date: Wed, 4 Dec 2024 20:14:34 +1300 Subject: [PATCH] minor refactor for RocksDB (#244) * minor refactor * fix --- Database/Package.resolved | 74 ++++++++++++++++++- Database/Package.swift | 4 + Database/Sources/Database/Database.swift | 2 - Database/Sources/Database/Options.swift | 48 ++++++++++++ Database/Sources/Database/RocksDB.swift | 60 +++++++-------- .../Sources/Database/RocksDBBackend.swift | 27 +++++++ Utils/Sources/Utils/Crypto/BLS.swift | 2 +- Utils/Sources/Utils/Crypto/Bandersnatch.swift | 2 +- Utils/Sources/Utils/SafePointer.swift | 12 ++- 9 files changed, 189 insertions(+), 42 deletions(-) delete mode 100644 Database/Sources/Database/Database.swift create mode 100644 Database/Sources/Database/Options.swift create mode 100644 Database/Sources/Database/RocksDBBackend.swift diff --git a/Database/Package.resolved b/Database/Package.resolved index 53426a59..b535f4a9 100644 --- a/Database/Package.resolved +++ b/Database/Package.resolved @@ -1,6 +1,78 @@ { - "originHash" : "45b033b1ce14d3e50b4dcbf706d87b3f7431c36dc9d513397f7be8a04999d298", + "originHash" : "5ea33c0ee52a6037b6b2321cf0e43f8b560504514f98c234c42c9b035a87752a", "pins" : [ + { + "identity" : "blake2.swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/tesseract-one/Blake2.swift.git", + "state" : { + "revision" : "29c55c8fe42d6661e5a32cc5bbbad1fff64fd01e", + "version" : "0.2.0" + } + }, + { + "identity" : "swift-asn1", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-asn1.git", + "state" : { + "revision" : "7faebca1ea4f9aaf0cda1cef7c43aecd2311ddf6", + "version" : "1.3.0" + } + }, + { + "identity" : "swift-crypto", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-crypto.git", + "state" : { + "revision" : "ff0f781cf7c6a22d52957e50b104f5768b50c779", + "version" : "3.10.0" + } + }, + { + "identity" : "swift-distributed-tracing", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-distributed-tracing.git", + "state" : { + "revision" : "6483d340853a944c96dbcc28b27dd10b6c581703", + "version" : "1.1.2" + } + }, + { + "identity" : "swift-log", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-log.git", + "state" : { + "revision" : "96a2f8a0fa41e9e09af4585e2724c4e825410b91", + "version" : "1.6.2" + } + }, + { + "identity" : "swift-metrics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-metrics.git", + "state" : { + "revision" : "e0165b53d49b413dd987526b641e05e246782685", + "version" : "2.5.0" + } + }, + { + "identity" : "swift-numerics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-numerics.git", + "state" : { + "branch" : "main", + "revision" : "e30276bff2ff5ed80566fbdca49f50aa160b0e83" + } + }, + { + "identity" : "swift-service-context", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-service-context.git", + "state" : { + "revision" : "0c62c5b4601d6c125050b5c3a97f20cce881d32b", + "version" : "1.1.0" + } + }, { "identity" : "swift-syntax", "kind" : "remoteSourceControl", diff --git a/Database/Package.swift b/Database/Package.swift index 2bdc378a..92d8e400 100644 --- a/Database/Package.swift +++ b/Database/Package.swift @@ -16,6 +16,8 @@ let package = Package( ), ], dependencies: [ + .package(path: "../Blockchain"), + .package(path: "../Utils"), .package(url: "https://github.com/apple/swift-testing.git", branch: "0.10.0"), ], targets: [ @@ -25,6 +27,8 @@ let package = Package( name: "Database", dependencies: [ "rocksdb", + "Blockchain", + "Utils", ], linkerSettings: [ .unsafeFlags(["-L../.lib", "-L/opt/homebrew/lib"]), diff --git a/Database/Sources/Database/Database.swift b/Database/Sources/Database/Database.swift deleted file mode 100644 index 08b22b80..00000000 --- a/Database/Sources/Database/Database.swift +++ /dev/null @@ -1,2 +0,0 @@ -// The Swift Programming Language -// https://docs.swift.org/swift-book diff --git a/Database/Sources/Database/Options.swift b/Database/Sources/Database/Options.swift new file mode 100644 index 00000000..8c67d8f2 --- /dev/null +++ b/Database/Sources/Database/Options.swift @@ -0,0 +1,48 @@ +import rocksdb +import Utils + +public struct Options: ~Copyable, Sendable { + let ptr: SafePointer + + var value: OpaquePointer { ptr.value } + + public init() { + ptr = .init(ptr: rocksdb_options_create(), free: rocksdb_options_destroy) + } + + public func increaseParallelism(cpus: Int) { + rocksdb_options_increase_parallelism(ptr.value, Int32(cpus)) + } + + public func optimizeLevelStyleCompaction(memtableMemoryBudget: UInt64) { + rocksdb_options_optimize_level_style_compaction(ptr.value, memtableMemoryBudget) + } + + public func setCreateIfMissing(_ createIfMissing: Bool) { + rocksdb_options_set_create_if_missing(ptr.value, createIfMissing ? 1 : 0) + } + + public func setLevelCompactionDynamicLevelBytes(levelCompactionDynamicLevelBytes: Bool) { + rocksdb_options_set_level_compaction_dynamic_level_bytes(ptr.value, levelCompactionDynamicLevelBytes ? 1 : 0) + } +} + +public struct WriteOptions: ~Copyable, Sendable { + let ptr: SafePointer + + var value: OpaquePointer { ptr.value } + + public init() { + ptr = .init(ptr: rocksdb_writeoptions_create(), free: rocksdb_writeoptions_destroy) + } +} + +public struct ReadOptions: ~Copyable, Sendable { + let ptr: SafePointer + + var value: OpaquePointer { ptr.value } + + public init() { + ptr = .init(ptr: rocksdb_readoptions_create(), free: rocksdb_readoptions_destroy) + } +} diff --git a/Database/Sources/Database/RocksDB.swift b/Database/Sources/Database/RocksDB.swift index dd73f824..f98d2d34 100644 --- a/Database/Sources/Database/RocksDB.swift +++ b/Database/Sources/Database/RocksDB.swift @@ -1,7 +1,8 @@ import Foundation import rocksdb +import Utils -public final class RocksDB { +public final class RocksDB: Sendable { public enum BatchOperation { case delete(key: Data) case put(key: Data, value: Data) @@ -16,42 +17,37 @@ public final class RocksDB { case noData } - private let dbOptions: OpaquePointer - private let writeOptions: OpaquePointer - private let readOptions: OpaquePointer - private let db: OpaquePointer + private let dbOptions: Options + private let writeOptions: WriteOptions + private let readOptions: ReadOptions + private let db: SendableOpaquePointer public init(path: URL) throws(Error) { - let dbOptions = rocksdb_options_create() - self.dbOptions = dbOptions! - let cpus = sysconf(Int32(_SC_NPROCESSORS_ONLN)) - - // Optimize rocksdb - rocksdb_options_increase_parallelism(dbOptions, Int32(cpus)) - let memtable_memory_budget: UInt64 = 512 * 1024 * 1024 // 512 MB - rocksdb_options_optimize_level_style_compaction(dbOptions, memtable_memory_budget) + let dbOptions = Options() - // create the DB if it's not already present - rocksdb_options_set_create_if_missing(dbOptions, 1) + // TODO: starting from options here + // https://github.com/paritytech/parity-common/blob/e3787dc768b08e10809834c65419ad3c255b5cac/kvdb-rocksdb/src/lib.rs#L339 - // create writeoptions - writeOptions = rocksdb_writeoptions_create() - // create readoptions - readOptions = rocksdb_readoptions_create() + let cpus = sysconf(Int32(_SC_NPROCESSORS_ONLN)) + dbOptions.increaseParallelism(cpus: cpus) + dbOptions.optimizeLevelStyleCompaction(memtableMemoryBudget: 512 * 1024 * 1024) // 512 MB + dbOptions.setCreateIfMissing(true) // open DB db = try Self.call { err, _ in - rocksdb_open(dbOptions, path.path, &err) + rocksdb_open(dbOptions.value, path.path, &err).asSendable } onErr: { message throws(Error) in throw Error.openFailed(message: message) } + + self.dbOptions = dbOptions + + writeOptions = WriteOptions() + readOptions = ReadOptions() } deinit { - rocksdb_writeoptions_destroy(writeOptions) - rocksdb_readoptions_destroy(readOptions) - rocksdb_options_destroy(dbOptions) - rocksdb_close(db) + rocksdb_close(db.value) } } @@ -61,8 +57,6 @@ extension RocksDB { private static func call( _ data: [Data], fn: (inout UnsafeMutablePointer?, [(ptr: UnsafeRawPointer, count: Int)]) -> R, - // need new swiftlint version https://github.com/realm/SwiftLint/issues/5631 - // swiftlint:disable:next identifier_name onErr: (String) throws(Error) -> Void ) throws(Error) -> R { var err: UnsafeMutablePointer? @@ -127,7 +121,7 @@ extension RocksDB { try Self.call(key, value) { err, ptrs in let key = ptrs[0] let value = ptrs[1] - rocksdb_put(db, writeOptions, key.ptr, key.count, value.ptr, value.count, &err) + rocksdb_put(db.value, writeOptions.value, key.ptr, key.count, value.ptr, value.count, &err) } onErr: { message throws(Error) in throw Error.putFailed(message: message) } @@ -138,22 +132,18 @@ extension RocksDB { let ret = try Self.call(key) { err, ptrs in let key = ptrs[0] - return rocksdb_get(db, readOptions, key.ptr, key.count, &len, &err) + return rocksdb_get(db.value, readOptions.value, key.ptr, key.count, &len, &err) } onErr: { message throws(Error) in throw Error.getFailed(message: message) } - defer { - free(ret) - } - - return ret.map { Data(bytes: $0, count: len) } + return ret.map { Data(bytesNoCopy: $0, count: len, deallocator: .free) } } public func delete(key: Data) throws { try Self.call(key) { err, ptrs in let key = ptrs[0] - rocksdb_delete(db, writeOptions, key.ptr, key.count, &err) + rocksdb_delete(db.value, writeOptions.value, key.ptr, key.count, &err) } onErr: { message throws(Error) in throw Error.deleteFailed(message: message) } @@ -182,7 +172,7 @@ extension RocksDB { } try Self.call { err, _ in - rocksdb_write(db, writeOptions, writeBatch, &err) + rocksdb_write(db.value, writeOptions.value, writeBatch, &err) } onErr: { message throws(Error) in throw Error.batchFailed(message: message) } diff --git a/Database/Sources/Database/RocksDBBackend.swift b/Database/Sources/Database/RocksDBBackend.swift new file mode 100644 index 00000000..78f02e12 --- /dev/null +++ b/Database/Sources/Database/RocksDBBackend.swift @@ -0,0 +1,27 @@ +import Blockchain +import Foundation +import Utils + +public final class RocksDBBackend: StateBackendProtocol { + public init() {} + + public func read(key _: Data) async throws -> Data? { + fatalError("unimplemented") + } + + public func readAll(prefix _: Data, startKey _: Data?, limit _: UInt32?) async throws -> [(key: Data, value: Data)] { + fatalError("unimplemented") + } + + public func batchUpdate(_: [StateBackendOperation]) async throws { + fatalError("unimplemented") + } + + public func readValue(hash _: Data32) async throws -> Data? { + fatalError("unimplemented") + } + + public func gc(callback _: @Sendable (Data) -> Data32?) async throws { + fatalError("unimplemented") + } +} diff --git a/Utils/Sources/Utils/Crypto/BLS.swift b/Utils/Sources/Utils/Crypto/BLS.swift index 9e10d5d3..d22a569e 100644 --- a/Utils/Sources/Utils/Crypto/BLS.swift +++ b/Utils/Sources/Utils/Crypto/BLS.swift @@ -30,7 +30,7 @@ public enum BLS: KeyType { } // use SafePointer to ensure keypair is freed even `try PublicKey` throws - keyPairPtr = SafePointer(ptr: ptr.asSendable, free: keypair_free) + keyPairPtr = SafePointer(ptr: ptr, free: keypair_free) publicKey = try PublicKey(keyPair: keyPairPtr.ptr.value) } diff --git a/Utils/Sources/Utils/Crypto/Bandersnatch.swift b/Utils/Sources/Utils/Crypto/Bandersnatch.swift index cbd4cd1f..f1b83129 100644 --- a/Utils/Sources/Utils/Crypto/Bandersnatch.swift +++ b/Utils/Sources/Utils/Crypto/Bandersnatch.swift @@ -29,7 +29,7 @@ public enum Bandersnatch: KeyType { throw .createSecretFailed(err) } - self.ptr = SafePointer(ptr: ptr.asSendable, free: secret_free) + self.ptr = SafePointer(ptr: ptr, free: secret_free) publicKey = try PublicKey(secretKey: self.ptr.ptr.value) } diff --git a/Utils/Sources/Utils/SafePointer.swift b/Utils/Sources/Utils/SafePointer.swift index 369ab63f..2ed06200 100644 --- a/Utils/Sources/Utils/SafePointer.swift +++ b/Utils/Sources/Utils/SafePointer.swift @@ -1,5 +1,13 @@ public struct SafePointer: ~Copyable, Sendable { - let ptr: SendableOpaquePointer - let free: @Sendable (_ ptr: OpaquePointer) -> Void + public let ptr: SendableOpaquePointer + private let free: @Sendable (_ ptr: OpaquePointer) -> Void + + public var value: OpaquePointer { ptr.value } + + public init(ptr: OpaquePointer, free: @Sendable @escaping (OpaquePointer) -> Void) { + self.ptr = ptr.asSendable + self.free = free + } + deinit { free(ptr.value) } }