nitro icon indicating copy to clipboard operation
nitro copied to clipboard

Offer function to invalidate cached function

Open iBobik opened this issue 1 year ago • 5 comments

Describe the feature

I some cases I will know cache is invalid.

const { call: getAccessToken, invalidate: invalidateAccessToken } = defineCachedFunction(async () => {
  const data = await fetch<any>('as/iam/authenticate', {

or

const getAccessToken = defineCachedFunction(async () => {
  const data = await fetch<any>('as/iam/authenticate', {
  …
}, {
  maxAge: 595,
  name: 'accessToken',
})


invalidateCachedFunction('accessToken')

Additional information

  • [ ] Would you be willing to help implement this feature?

iBobik avatar Mar 04 '24 19:03 iBobik

Is this what you're looking for ? https://github.com/unjs/nitro/blob/45f212bea448c69e5e9e8b069d0036611b80899f/examples/cached-handler/routes/index.ts#L5

but shouldInvalidateCache instead of shouldBypassCache ?

adrienZ avatar Mar 05 '24 18:03 adrienZ

No, I need to clear cache when I get info about in different context.

5. 3. 2024 19:04:39, Adrien Zaganelli @.***> napsal:

I think this is what you're looking for :)

https://github.com/unjs/nitro/blob/45f212bea448c69e5e9e8b069d0036611b80899f/examples/cached-handler/routes/index.ts#L5

— Reply to this email directly, view it on GitHub https://github.com/unjs/nitro/issues/2218#issuecomment-1979343106, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAEV6WD2YZNWPE2FTLQ427TYWYCLPAVCNFSM6AAAAABEF2HVD2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSNZZGM2DGMJQGY . You are receiving this because you authored the thread.Message ID: @.***>

iBobik avatar Mar 05 '24 19:03 iBobik

When using defineCahedFunction, Nitro will generate a specific key (learn more):

`${options.group}:${options.name}:${options.getKey(...args)}.json`
  • Default of group is 'nitro:functions'
  • Default of name is '_'

So if you have the following:

const getAccessToken = defineCachedFunction(() => {
  return String(Date.now())
}, {
  maxAge: 10,
  name: 'getAccessToken',
  getKey: () => 'default'
})

It will save in the storage the following key:

nitro:functions:getAccessToken:default.json

You can access the cache storage using:

const cache = useStorage('cache')

Then if you want to invalidate the cached function:

const cache = useStorage('cache')
await cache.removeItem('nitro:functions:getAccessToken:default.json')

Hope that helps 😊

I think a nice option would be to expose a .invalidate() method (or we can find a better name) for it. We could also expose some .meta() to know more about the cache options, key and next expiration.

const getAccessToken = defineCachedFunction(() => {
  return String(Date.now())
}, {
  maxAge: 10,
  name: 'getAccessToken',
  getKey: () => 'default'
})

// Get the meta
await getAccessToken.meta()
// Remove the entry in the cache storage
await getAccessToken.invalidate()

I think @pi0 may have some great ideas around it 💚

atinux avatar Mar 06 '24 13:03 atinux

Another practical way would be:

const getAccessToken = defineCachedFunction((param: string) => {
  return encode(param + String(Date.now()))
}, {
  maxAge: 10,
})

await getAccessToken("foo") // cache miss
await getAccessToken("foo") // cache hit
await getAccessToken.withBypass("foo") // same as if shouldBypassCache() returned true
await getAccessToken.withInvalidate("foo") // same as if shouldInvalidateCache() returned true

I am using a wrapper for defineCachedFunction adding the above:

import type { CacheOptions } from "nitropack/types"

type CachedFunctionWithInvalidate<T, ArgsT extends unknown[] = any[]> = ((...args: ArgsT) => Promise<T>) & {
  withInvalidate(...args: ArgsT): Promise<T>
}

export function defineCachedFunctionWithInvalidate<T, ArgsT extends unknown[] = any[]>(fn: (...args: ArgsT) => T | Promise<T>, opts?: CacheOptions<T, ArgsT>) {
  let invalidated = false

  const cachedFunction = defineCachedFunction(fn, {
    ...opts,
    shouldInvalidateCache(...args) {
      if (invalidated) {
        invalidated = false
        return true
      }
      return opts?.shouldInvalidateCache ? opts.shouldInvalidateCache(...args) : false
    },
  }) as CachedFunctionWithInvalidate<T, ArgsT>

  cachedFunction.withInvalidate = (...args: ArgsT) => {
    invalidated = true
    return cachedFunction(...args)
  }

  return cachedFunction
}

then:

const getAccessToken = defineCachedFunctionWithInvalidate((param: string) => { ... })

await getAccessToken("foo") // normal use
await getAccessToken.withInvalidate("foo") // force invalidate

IlyaSemenov avatar May 12 '25 09:05 IlyaSemenov