Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol. wisp.place
86
fork

Configure Feed

Select the types of activity you want to include in your feed.

sync lastAccessed back to the in-memory metadataIndex

authored by

nekomimi.pet and committed by
Tangled
21178540 bd8e5367

+77
+9
packages/@wispplace/tiered-storage/src/tiers/DiskStorageTier.ts
··· 482 482 } 483 483 484 484 await this.writeMetadataAtomically(metaPath, metadata) 485 + 486 + // Keep in-memory index in sync so eviction (LRU sort) sees updated lastAccessed 487 + const entry = this.metadataIndex.get(key) 488 + if (entry) { 489 + entry.lastAccessed = metadata.lastAccessed 490 + if (metadata.ttl) { 491 + entry.ttl = metadata.ttl 492 + } 493 + } 485 494 } 486 495 487 496 async getStats(): Promise<TierStats> {
+68
packages/@wispplace/tiered-storage/test/DiskStorageTier.gc.test.ts
··· 190 190 }) 191 191 }) 192 192 }) 193 + 194 + describe('DiskStorageTier - LRU eviction respects setMetadata updates', () => { 195 + const lruTestDir = './test-disk-lru' 196 + 197 + beforeEach(async () => { 198 + await rm(lruTestDir, { recursive: true, force: true }) 199 + }) 200 + 201 + afterAll(async () => { 202 + await rm(lruTestDir, { recursive: true, force: true }) 203 + }) 204 + 205 + test('setMetadata updates lastAccessed on disk so LRU order survives restart', async () => { 206 + const data = new TextEncoder().encode('x'.repeat(80)) 207 + 208 + const tier1 = new DiskStorageTier({ directory: lruTestDir, maxSizeBytes: 200, evictionPolicy: 'lru' }) 209 + 210 + const old = new Date(Date.now() - 3000) 211 + const mid = new Date(Date.now() - 2000) 212 + 213 + await tier1.set('a', data, { ...makeMetadata('a', data.byteLength), lastAccessed: old }) 214 + await tier1.set('b', data, { ...makeMetadata('b', data.byteLength), lastAccessed: mid }) 215 + 216 + // Touch 'a' so its lastAccessed is now newest — persisted to disk via setMetadata 217 + const metaA = (await tier1.getMetadata('a'))! 218 + metaA.lastAccessed = new Date() 219 + await tier1.setMetadata('a', metaA) 220 + 221 + // Simulate restart — new instance rebuilds index from disk 222 + const tier2 = new DiskStorageTier({ directory: lruTestDir, maxSizeBytes: 200, evictionPolicy: 'lru' }) 223 + await new Promise((resolve) => setTimeout(resolve, 100)) // let rebuildIndex complete 224 + 225 + // 'b' is now LRU; writing a third entry should evict 'b', not 'a' 226 + await tier2.set('c', data, makeMetadata('c', data.byteLength)) 227 + 228 + expect(await tier2.exists('a')).toBe(true) 229 + expect(await tier2.exists('b')).toBe(false) 230 + expect(await tier2.exists('c')).toBe(true) 231 + }) 232 + 233 + test('recently-accessed entry survives eviction over stale entries', async () => { 234 + // Capacity: 200 bytes, each entry is 80 bytes. 235 + // Write 'a' (old) and 'b' (mid) — 160 bytes total, fits fine. 236 + // Touch 'a' to make it freshest. Then write 'c' (160+80=240 > 200) → eviction. 237 + // 'b' is now the LRU victim; 'a' must survive. 238 + const tier = new DiskStorageTier({ directory: lruTestDir, maxSizeBytes: 200, evictionPolicy: 'lru' }) 239 + const data = new TextEncoder().encode('x'.repeat(80)) 240 + 241 + const old = new Date(Date.now() - 3000) 242 + const mid = new Date(Date.now() - 2000) 243 + 244 + await tier.set('a', data, { ...makeMetadata('a', data.byteLength), lastAccessed: old }) 245 + await tier.set('b', data, { ...makeMetadata('b', data.byteLength), lastAccessed: mid }) 246 + 247 + // Touch 'a' so it becomes the most recently accessed 248 + const metaA = (await tier.getMetadata('a'))! 249 + metaA.lastAccessed = new Date() 250 + await tier.setMetadata('a', metaA) 251 + 252 + // Writing 'c' triggers eviction. Without the fix, 'a' would be evicted 253 + // (stale timestamp in metadataIndex). With the fix, 'b' is evicted instead. 254 + await tier.set('c', data, makeMetadata('c', data.byteLength)) 255 + 256 + expect(await tier.exists('a')).toBe(true) // freshly touched — must survive 257 + expect(await tier.exists('b')).toBe(false) // true LRU victim 258 + expect(await tier.exists('c')).toBe(true) 259 + }) 260 + })