Skip to content

Commit ac61468

Browse files
authored
Use existing stores if available (#460)
1 parent 822859b commit ac61468

File tree

2 files changed

+74
-8
lines changed

2 files changed

+74
-8
lines changed

src/datastore/LMDB.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -152,15 +152,20 @@ export class LMDBStoreFactory implements DataStoreFactory {
152152
private recreateStores(): void {
153153
for (const name of this.storeNames) {
154154
const database = this.env.openDB<unknown, string>({ name, encoding: Encoding });
155-
this.stores.set(
156-
name,
157-
new LMDBStore(
155+
const existing = this.stores.get(name);
156+
if (existing) {
157+
existing.updateStore(database);
158+
} else {
159+
this.stores.set(
158160
name,
159-
database,
160-
(e) => this.handleError(e),
161-
() => this.ensureValidEnv(),
162-
),
163-
);
161+
new LMDBStore(
162+
name,
163+
database,
164+
(e) => this.handleError(e),
165+
() => this.ensureValidEnv(),
166+
),
167+
);
168+
}
164169
}
165170
}
166171

tst/unit/datastore/LMDB.recovery.test.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,67 @@ describe('LMDB fork detection and recovery', () => {
117117
});
118118
});
119119

120+
describe('cached reference stability', () => {
121+
it('should keep cached store references working after recoverFromFork', async () => {
122+
// Simulate SchemaStore pattern: grab reference once, hold it
123+
const cachedRef = factory.get(StoreName.public_schemas);
124+
await cachedRef.put('before', 'fork');
125+
126+
// Trigger recoverFromFork via handleError with fork-style error
127+
const handleError = (factory as any).handleError.bind(factory);
128+
Object.defineProperty(process, 'pid', { value: originalPid + 1000, configurable: true });
129+
handleError(new Error("doesn't match env pid"));
130+
131+
// The cached reference must still work — not be a dangling old object
132+
expect(cachedRef.get('before')).toBe('fork');
133+
await cachedRef.put('after', 'fork');
134+
expect(cachedRef.get('after')).toBe('fork');
135+
});
136+
137+
it('should keep cached store references working after recoverFromError', async () => {
138+
const cachedRef = factory.get(StoreName.public_schemas);
139+
await cachedRef.put('before', 'error');
140+
141+
// Trigger recoverFromError via handleError with a generic error
142+
const reopenSpy = vi.spyOn(factory as any, 'reopenEnv');
143+
const handleError = (factory as any).handleError.bind(factory);
144+
handleError(new Error('MDB_CORRUPTED: some corruption'));
145+
146+
expect(reopenSpy).toHaveBeenCalled();
147+
148+
// The cached reference must still be the same object and functional
149+
expect(factory.get(StoreName.public_schemas)).toBe(cachedRef);
150+
await cachedRef.put('after', 'error');
151+
expect(cachedRef.get('after')).toBe('error');
152+
153+
reopenSpy.mockRestore();
154+
});
155+
156+
it('should preserve object identity of stores after recreateStores', () => {
157+
const refBefore = factory.get(StoreName.public_schemas);
158+
const samRefBefore = factory.get(StoreName.sam_schemas);
159+
160+
// Directly call recreateStores
161+
(factory as any).recreateStores();
162+
163+
// factory.get() must return the exact same object
164+
expect(factory.get(StoreName.public_schemas)).toBe(refBefore);
165+
expect(factory.get(StoreName.sam_schemas)).toBe(samRefBefore);
166+
});
167+
168+
it('should allow reads and writes on cached reference after recreateStores', async () => {
169+
const cachedRef = factory.get(StoreName.public_schemas);
170+
await cachedRef.put('key', 'value');
171+
172+
(factory as any).recreateStores();
173+
174+
// Cached ref should still read/write correctly
175+
expect(cachedRef.get('key')).toBe('value');
176+
await cachedRef.put('key2', 'value2');
177+
expect(cachedRef.get('key2')).toBe('value2');
178+
});
179+
});
180+
120181
describe('factory behavior', () => {
121182
it('should throw for unknown store name', () => {
122183
expect(() => factory.get('unknown-store' as StoreName)).toThrow('Store unknown-store not found');

0 commit comments

Comments
 (0)