This repository has been archived by the owner on Aug 3, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 155
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ability to use KV for persistent cache on workers
- Loading branch information
Showing
7 changed files
with
180 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import type { StorageMounts } from 'nitropack' | ||
|
||
// https://nuxt.com/docs/api/configuration/nuxt-config | ||
declare global { | ||
namespace NodeJS { | ||
interface ProcessEnv { | ||
/** Preset used to build Nitro (provided manually). */ | ||
NITRO_PRESET?: string | ||
|
||
/** Cloudflare KV via binding: name of the binding. */ | ||
CF_KV_BINDING_CACHE?: string | ||
|
||
/** Vercel KV: token. */ | ||
KV_REST_API_TOKEN?: string | ||
/** Vercel KV: API URL. */ | ||
KV_REST_API_URL?: string | ||
/** | ||
* Vercel KV: base name for cache KV. | ||
* @default 'cache' | ||
*/ | ||
VERCEL_KV_CACHE_BASE?: string | ||
|
||
/** Cache storage option. */ | ||
CACHE_STORAGE_OPTION?: string | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Checks that all environment variables are defined. | ||
* @param vars Variables to check. | ||
* @returns All missing variables. | ||
*/ | ||
function getMissingVars(vars: string[]) { | ||
return vars.filter((varName) => !process.env[varName]) | ||
} | ||
|
||
/** | ||
* Returns Nitro storage mounts or nothing. | ||
*/ | ||
function getCacheStorageMount(): StorageMounts[string] | undefined { | ||
switch (process.env.CACHE_STORAGE_OPTION) { | ||
case 'cloudflare-kv': { | ||
if (process.env.CF_KV_BINDING_CACHE) { | ||
return { | ||
driver: '~/server/storage/cached-cloudflare-kv-binding', | ||
binding: process.env.CF_KV_BINDING_CACHE, | ||
} | ||
} | ||
|
||
console.warn( | ||
'You wanted to use `cloudflare-kv` cache store option, however you have not provided `CF_KV_BINDING_CACHE` environment variable. The cache will use in-memory storage that is not persistent in workers.' | ||
) | ||
|
||
break | ||
} | ||
case 'vercel-kv': { | ||
const missingVars = getMissingVars(['KV_REST_API_TOKEN', 'KV_REST_API_URL']) | ||
|
||
if (!missingVars.length) { | ||
return { | ||
driver: '~/server/storage/cached-vercel-kv', | ||
base: process.env.VERCEL_KV_CACHE_BASE || 'cache', | ||
url: process.env.KV_REST_API_URL, | ||
token: process.env.KV_REST_API_TOKEN, | ||
env: false, | ||
} | ||
} | ||
|
||
console.log( | ||
`You wanted to use \`vercel-kv\` cache store option, however you have not provided ${missingVars | ||
.map((varName) => `\`${varName}\``) | ||
.join( | ||
', ' | ||
)} environment variable. The cache will use in-memory storage taht is not persistent in serverless functions.` | ||
) | ||
|
||
break | ||
} | ||
} | ||
|
||
return undefined | ||
} | ||
|
||
export function getStorageMounts(): StorageMounts | undefined { | ||
let mounts: StorageMounts | undefined | ||
|
||
const cacheMount = getCacheStorageMount() | ||
if (cacheMount != null) (mounts ??= {}).cache = cacheMount | ||
|
||
return mounts | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import cloudflareKVStorage, { KVOptions } from 'unstorage/drivers/cloudflare-kv-binding' | ||
import { Driver } from 'unstorage' | ||
import cachedDriver from './cached.ts' | ||
|
||
export default function cachedVercelKV(opts: KVOptions): Driver { | ||
return cachedDriver({ driver: cloudflareKVStorage(opts) }) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import vercelStorage, { VercelKVOptions } from 'unstorage/drivers/vercel-kv' | ||
import { Driver } from 'unstorage' | ||
import cachedDriver from './cached.ts' | ||
|
||
export default function cachedVercelKV(opts: VercelKVOptions): Driver { | ||
return cachedDriver({ driver: vercelStorage(opts) }) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { Driver } from 'unstorage' | ||
import memoryDriver from 'unstorage/drivers/memory' | ||
|
||
export interface CachedOptions { | ||
driver: Driver | ||
} | ||
|
||
export default function cached(options: CachedOptions): Driver { | ||
const { driver } = options | ||
const memory = memoryDriver() as Driver | ||
return { | ||
...driver, | ||
name: driver.name ? `cached:${driver.name}` : `cached`, | ||
options, | ||
async hasItem(key) { | ||
return (await memory.hasItem(key, {})) || (await driver.hasItem(key, {})) | ||
}, | ||
async getItem(key) { | ||
const memoryLookup = await memory.getItem(key) | ||
if (memoryLookup !== null) return memoryLookup | ||
|
||
const lookup = await driver.getItem(key) | ||
memory.setItem!(key, lookup as any, {}) | ||
return lookup | ||
}, | ||
async setItem(key, value) { | ||
memory.setItem!(key, value, {}) | ||
await driver.setItem?.(key, value, {}) | ||
}, | ||
} | ||
} |