キャッシュ
AdonisJS Cache(@adonisjs/cache
)は、bentocache.dev 上に構築されたシンプルで軽量なラッパーです。データをキャッシュし、アプリケーションのパフォーマンスを向上させます。Redis、DynamoDB、PostgreSQL、インメモリキャッシュなど、さまざまなキャッシュドライバーとやり取りするための統一されたAPIを提供します。
Bentocacheのドキュメントもぜひご覧ください。Bentocacheは、マルチティア、グレース期間、タグ付け、タイムアウト、スタンピードプロテクション など、状況によって非常に便利な高度なオプション機能を提供しています。
インストール
以下のコマンドで @adonisjs/cache
パッケージをインストール・設定します。
node ace add @adonisjs/cache
- 検出されたパッケージマネージャーを使って
@adonisjs/cache
パッケージをインストールします。 adonisrc.ts
ファイルに以下のサービスプロバイダーを登録します。
{
providers: [
// ...他のプロバイダー
() => import('@adonisjs/cache/cache_provider'),
]
}
config/cache.ts
ファイルを作成します。- 選択したキャッシュドライバー用の環境変数を
.env
ファイルに定義します。
設定
キャッシュパッケージの設定ファイルは config/cache.ts
にあります。デフォルトのキャッシュドライバーや、利用可能なドライバー、その個別設定を行えます。
参考: Config stub
import { defineConfig, store, drivers } from '@adonisjs/cache'
const cacheConfig = defineConfig({
default: 'redis',
stores: {
/**
* DynamoDB のみでキャッシュ
*/
dynamodb: store().useL2Layer(drivers.dynamodb({})),
/**
* Lucidで設定したデータベースを利用
*/
database: store().useL2Layer(drivers.database({ connectionName: 'default' })),
/**
* プライマリストアにインメモリ、セカンダリストアにRedisを利用
* 複数サーバーで動作する場合、インメモリキャッシュはbusで同期が必要
*/
redis: store()
.useL1Layer(drivers.memory({ maxSize: '100mb' }))
.useL2Layer(drivers.redis({ connectionName: 'main' }))
.useBus(drivers.redisBus({ connectionName: 'main' })),
},
})
export default cacheConfig
上記の例では、各キャッシュストアに複数のレイヤーを設定しています。これはマルチティアキャッシュシステムと呼ばれ、まず高速なインメモリキャッシュ(第一層)を確認し、見つからなければ分散キャッシュ(第二層)を利用します。
Redis
Redisをキャッシュシステムとして利用するには、@adonisjs/redis
パッケージのインストールと設定が必要です。詳細はRedisを参照してください。
config/cache.ts
では connectionName
を指定します。この値は config/redis.ts
の設定キーと一致させてください。
データベース
database
ドライバーは @adonisjs/lucid
が必要です。利用する場合はインストールと設定を行ってください。
config/cache.ts
では connectionName
を指定します。この値は config/database.ts
の設定キーと一致させてください。
その他のドライバー
memory
、dynamodb
、kysely
、orchid
など他のドライバーも利用できます。
詳細は Cache Drivers を参照してください。
使い方
キャッシュの設定後、cache
サービスをインポートして利用できます。以下はユーザー情報を5分間キャッシュする例です。
import cache from '@adonisjs/cache/services/main'
import router from '@adonisjs/core/services/router'
router.get('/user/:id', async ({ params }) => {
return cache.getOrSet({
key: `user:${params.id}`,
factory: async () => {
const user = await User.find(params.id)
return user.toJSON()
},
ttl: '5m',
})
})
ご覧の通り、user.toJSON()
でユーザーデータをシリアライズしています。キャッシュに保存するにはデータのシリアライズが必要です。Lucidモデルや Date
インスタンスなどは直接キャッシュできません。
ttl
はキャッシュキーの有効期間(Time To Live)です。期限切れ後はキャッシュが無効となり、次のリクエストでfactoryメソッドから再取得されます。
タグ付け
キャッシュエントリにタグを付与し、タグ単位で一括無効化できます。
await bento.getOrSet({
key: 'foo',
factory: getFromDb(),
tags: ['tag-1', 'tag-2']
});
await bento.deleteByTag({ tags: ['tag-1'] });
名前空間
キーをグループ化するもう一つの方法が名前空間です。後からまとめて無効化できます。
const users = bento.namespace('users')
users.set({ key: '32', value: { name: 'foo' } })
users.set({ key: '33', value: { name: 'bar' } })
users.clear()
グレース期間
grace
オプションを使うと、キャッシュキーが期限切れでもグレース期間内なら古いデータを返しつつ裏で再取得できます。SWR
や TanStack Query
と同様の動作です。
import cache from '@adonisjs/cache/services/main'
cache.getOrSet({
key: 'slow-api',
factory: async () => {
await sleep(5000)
return 'slow-api-response'
},
ttl: '1h',
grace: '6h',
})
上記では、1時間でデータは期限切れとなりますが、6時間のグレース期間内なら古いデータを返しつつ裏で再取得・更新します。
タイムアウト
timeout
オプションでfactoryメソッドの最大実行時間を設定できます。デフォルトは0ms(常に古いデータを返しつつ裏で再取得)。
import cache from '@adonisjs/cache/services/main'
cache.getOrSet({
key: 'slow-api',
factory: async () => {
await sleep(5000)
return 'slow-api-response'
},
ttl: '1h',
grace: '6h',
timeout: '200ms',
})
この例では、factoryメソッドは最大200msまで実行されます。超過した場合は古いデータを返し、裏でfactoryメソッドが継続します。
grace
を定義しない場合でも、hardTimeout
でfactoryメソッドの実行を強制終了できます。
import cache from '@adonisjs/cache/services/main'
cache.getOrSet({
key: 'slow-api',
factory: async () => {
await sleep(5000)
return 'slow-api-response'
},
ttl: '1h',
hardTimeout: '200ms',
})
この場合、200ms経過でfactoryメソッドは停止し、エラーがスローされます。
timeout
と hardTimeout
は同時に指定可能です。timeout
は古いデータを返すまでの最大時間、hardTimeout
はfactoryメソッドの最大実行時間です。
キャッシュサービス
@adonisjs/cache/services/main
からエクスポートされるキャッシュサービスは、config/cache.ts
の設定を使って作成された BentoCache クラスのシングルトンインスタンスです。
アプリケーション内でインポートして利用できます。
import cache from '@adonisjs/cache/services/main'
/**
* `use` メソッドを呼ばなければ、デフォルトストア(config/cache.tsで定義)を利用します。
*/
cache.put({ key: 'username', value: 'jul', ttl: '1h' })
/**
* `use` メソッドで、config/cache.tsで定義した他のストアを利用できます。
*/
cache.use('dynamodb').put({ key: 'username', value: 'jul', ttl: '1h' })
利用可能なメソッド一覧: BentoCache API
await cache.namespace('users').set({ key: 'username', value: 'jul' })
await cache.namespace('users').get({ key: 'username' })
await cache.get({ key: 'username' })
await cache.set({key: 'username', value: 'jul' })
await cache.setForever({ key: 'username', value:'jul' })
await cache.getOrSet({
key: 'username',
factory: async () => fetchUserName(),
ttl: '1h',
})
await cache.has({ key: 'username' })
await cache.missing({ key: 'username' })
await cache.pull({ key: 'username' })
await cache.delete({ key: 'username' })
await cache.deleteMany({ keys: ['products', 'users'] })
await cache.deleteByTag({ tags: ['products', 'users'] })
await cache.clear()
Edgeヘルパー
cache
サービスはEdgeテンプレート内でも利用できます。テンプレート内でキャッシュ値を直接取得できます。
<p>
Hello {{ await cache.get('username') }}
</p>
Aceコマンド
@adonisjs/cache
パッケージは、ターミナルからキャッシュ操作できるAceコマンドも提供します。
cache
指定したストアのキャッシュをクリアします。未指定の場合はデフォルトストアをクリアします。
# デフォルトキャッシュストアをクリア
node ace cache:clear
# 特定のキャッシュストアをクリア
node ace cache:clear redis
# 特定の名前空間をクリア
node ace cache:clear store --namespace users
# 複数のタグをクリア
node ace cache:clear store --tags products --tags users
cache
指定したストアから特定のキャッシュキーを削除します。未指定の場合はデフォルトストアから削除します。
# 特定のキャッシュキーを削除
node ace cache:delete cache-key
# 特定のストアからキャッシュキーを削除
node ace cache:delete cache-key store