mirror of
git://jb55.com/damus
synced 2024-09-16 02:03:45 +00:00
ndb/txn: inherit active transactions on the same thread
Many different parts of the codebase could be opening transactions when somewhere higher in the heirarchy on the main thread might already have an active transaction. This can lead to failed transaction opening which is bad. Instead of relying on passing down the transaction to subviews, lets keep track of the active transactions in a thread-local dictionary. That way whenever we create a new transaction we can inherit the one that is already active in the current thread. Inherited transactions don't end the query when they are garbage collected, we still expect the first-opened query to do this.
This commit is contained in:
parent
f6b59b3f5d
commit
4e447ddbed
@ -41,7 +41,7 @@ enum NdbData {
|
||||
|
||||
class NdbNote: Encodable, Equatable, Hashable {
|
||||
// we can have owned notes, but we can also have lmdb virtual-memory mapped notes so its optional
|
||||
private let owned: Bool
|
||||
let owned: Bool
|
||||
let count: Int
|
||||
let key: NoteKey?
|
||||
let note: UnsafeMutablePointer<ndb_note>
|
||||
|
@ -16,22 +16,32 @@ class NdbTxn<T> {
|
||||
var txn: ndb_txn
|
||||
private var val: T!
|
||||
var moved: Bool
|
||||
var inherited: Bool
|
||||
|
||||
init(ndb: Ndb, with: (NdbTxn<T>) -> T = { _ in () }) {
|
||||
self.txn = ndb_txn()
|
||||
#if TXNDEBUG
|
||||
txn_count += 1
|
||||
print("opening transaction \(txn_count)")
|
||||
#endif
|
||||
if let active_txn = Thread.current.threadDictionary["ndb_txn"] as? ndb_txn {
|
||||
// some parent thread is active, use that instead
|
||||
self.txn = active_txn
|
||||
self.inherited = true
|
||||
} else {
|
||||
self.txn = ndb_txn()
|
||||
let _ = ndb_begin_query(ndb.ndb.ndb, &self.txn)
|
||||
Thread.current.threadDictionary["ndb_txn"] = self.txn
|
||||
self.inherited = false
|
||||
}
|
||||
self.moved = false
|
||||
self.val = with(self)
|
||||
}
|
||||
|
||||
init(txn: ndb_txn, val: T) {
|
||||
private init(txn: ndb_txn, val: T) {
|
||||
self.txn = txn
|
||||
self.val = val
|
||||
self.moved = false
|
||||
self.inherited = false
|
||||
}
|
||||
|
||||
/// Only access temporarily! Do not store database references for longterm use. If it's a primitive type you
|
||||
@ -42,13 +52,16 @@ class NdbTxn<T> {
|
||||
}
|
||||
|
||||
deinit {
|
||||
if !moved {
|
||||
if moved || inherited {
|
||||
return
|
||||
}
|
||||
|
||||
#if TXNDEBUG
|
||||
txn_count -= 1;
|
||||
print("closing transaction \(txn_count)")
|
||||
#endif
|
||||
ndb_end_query(&self.txn)
|
||||
}
|
||||
Thread.current.threadDictionary.removeObject(forKey: "ndb_txn")
|
||||
}
|
||||
|
||||
// functor
|
||||
|
@ -156,6 +156,25 @@ final class NdbTests: XCTestCase {
|
||||
XCTAssertEqual(testNote.content, "https://cdn.nostr.build/i/5c1d3296f66c2630131bf123106486aeaf051ed8466031c0e0532d70b33cddb2.jpg")
|
||||
}
|
||||
|
||||
func test_inherited_transactions() throws {
|
||||
let ndb = Ndb(path: db_dir)!
|
||||
do {
|
||||
let txn1 = NdbTxn(ndb: ndb)
|
||||
|
||||
let ntxn = (Thread.current.threadDictionary.value(forKey: "ndb_txn") as? ndb_txn)!
|
||||
XCTAssertEqual(txn1.txn.lmdb, ntxn.lmdb)
|
||||
XCTAssertEqual(txn1.txn.mdb_txn, ntxn.mdb_txn)
|
||||
|
||||
let txn2 = NdbTxn(ndb: ndb)
|
||||
|
||||
XCTAssertEqual(txn1.inherited, false)
|
||||
XCTAssertEqual(txn2.inherited, true)
|
||||
}
|
||||
|
||||
let ndb_txn = Thread.current.threadDictionary.value(forKey: "ndb_txn")
|
||||
XCTAssertNil(ndb_txn)
|
||||
}
|
||||
|
||||
func test_decode_perf() throws {
|
||||
// This is an example of a performance test case.
|
||||
self.measure {
|
||||
|
Loading…
Reference in New Issue
Block a user