ipfs / helia

An implementation of IPFS in JavaScript
https://helia.io
Other
812 stars 81 forks source link

bug: ipns doesn't purge cache based on TTL #479

Closed SgtPooki closed 3 months ago

SgtPooki commented 3 months ago

need to resolve https://github.com/ipfs/helia/pull/473#discussion_r1541949303. Summary of comment below:

if we don't store something like cacheEOL or storedAt timestamp, then we have no way to, "[respect] the TTL value"

this is the current flow:

requesting record for the first time

  1. check if this.localStore has record // NO
  2. query network for the record.
  3. validate that record is correct
  4. store record in cache (has ttl)
  5. return record to user

later requests for record (with default nocache=undefined)

  1. check if this.localStore has record // yes
  2. return cached record

we essentially need a check between 1 & 2 of later requests for record that will check if the record is expired.. unless this.localStore is already handling expiry, but it doesn't seem like it is:

╰─ ✔ ❯ rg 'localStore' packages/ipns
packages/ipns/src/routing/local-store.ts
26:export function localStore (datastore: Datastore): LocalStore {

packages/ipns/src/index.ts
240:import { localStore, type LocalStore } from './routing/local-store.js'
406:  private readonly localStore: LocalStore
416:    this.localStore = localStore(components.datastore)
426:      if (await this.localStore.has(routingKey, options)) {
428:        const buf = await this.localStore.get(routingKey, options)
437:      await this.localStore.put(routingKey, marshaledRecord, options)
536:    const cached = await this.localStore.has(routingKey, options)
543:        const record = await this.localStore.get(routingKey, options)
604:    await this.localStore.put(routingKey, record, options)

packages/ipns/src/routing/pubsub.ts
10:import { localStore, type LocalStore } from './local-store.js'
34:  private readonly localStore: LocalStore
40:    this.localStore = localStore(components.datastore)
74:    if (await this.localStore.has(routingKey)) {
75:      const currentRecord = await this.localStore.get(routingKey)
91:    await this.localStore.put(routingKey, message.data)
131:      return await this.localStore.get(routingKey, options)

we have no calls to remove, and localStore doesn't have any methods other than put/get/has, and nothing called on the datastore (given in localStore creation) other than put/get/has