コンテンツにスキップ
UniKVS
Esc
↑↓移動↵開く⌘Jプレビュー
このページの内容

@unikvs/ttl

相対秒数や絶対時刻で TTL を付与し、期限切れを欠落として扱うミドルウェアです。

概要

Ttl は書き込んだ値に相対秒数や絶対時刻の有効期限を付与し、期限切れの読み取りを欠落として扱います。Middleware

短命なキャッシュやセッション的なデータの保持に使います。プレーン値とストリームで失効の管理方式が異なります。

  • name は Ttl です。
  • isOpen は常に true です。
  • 期限の指定は操作ごとの変数 @unikvs/ttl:ttl・@unikvs/ttl:expiresAt (非名前空間名 ttl・expiresAt も後方互換で受付) と、既定値 defaultTtl で行います。
  • 期限切れの get・has・stream・copy・move は KeyNotFoundError 相当として扱います。
  • copy と move は、このミドルウェアが適用された分岐で TTL の対象となるすべての宛先が committed になったときに限り、コピー元・移動元の有効期限をコピー先・移動先へ引き継ぎます。

インストールは次のコマンドで行います。

npm install @unikvs/ttl
pnpm add @unikvs/ttl
yarn add @unikvs/ttl
bun add @unikvs/ttl
nub add @unikvs/ttl
aube add @unikvs/ttl

使い方

Ttl は IMiddleware を実装しています。UniKvs には use() で渡します。

import { Ttl } from "@unikvs/ttl";

コンストラクターの引数は次のとおりです。

プロパティ型
defaultTtl?TtlInput

書き込みごとの指定がないときに使う既定の TTL です。相対秒数、絶対時刻、オブジェクト形式のいずれも指定できます。

型TtlInput
now?() => number

現在時刻 (epoch ミリ秒) を返す関数です。テストで時刻を固定するときに使います。

型() => number
デフォルトDate.now

以下は、既定の TTL を 60 秒に設定し、書き込みごとに上書きする例です。

import { Memory } from "@unikvs/memory";
import { Ttl } from "@unikvs/ttl";
import * as unikvs from "unikvs";

const kvs = new unikvs.Builder<{ report: unikvs.PlainValue<string> }>()
  .use(new Ttl({ defaultTtl: 60 }))
  .persist(new Memory())
  .build();

await kvs.open();
await kvs.set("report", "hello");
await kvs.set("short", "bye", { vars: { "@unikvs/ttl:ttl": 10 } });
console.log(await kvs.get("report")); // "hello"
await kvs.close();

Ttl

有効期限エンベロープと操作内記録で失効を管理するミドルウェアです。読み取り前に失効を判定し、存在確認では存在しないものとして扱います。

TtlOptions

Ttl のコンストラクターが受け取るオプションです。既定の TTL と時刻取得を指定します。

TtlInput

書き込みごとに指定できる TTL 値です。number は相対秒数 (小数も可、0 以下は即時失効)、Date は絶対失効時刻、オブジェクト形式では ttl に相対秒数、expiresAt に epoch ミリ秒または Date を指定します。

データ型

メソッド 入力 出力
encode unknown エンベロープか透過
decode エンベロープか任意値 unknown
encodeStream unknown (チャンク) unknown (チャンク、透過)
decodeStream unknown (チャンク) unknown (チャンク、透過)
  • encode は TTL があるときに { "@unikvs/ttl": 1, expiresAt: number, data: unknown } の形に包みます。TTL がない上書きではデータを透過させます。
  • decode は期限内であれば包みを剥がして元の値を返し、期限切れでは KeyNotFoundError を投げます。再起動後などで記録が欠けているときはエンベロープから記録を復元します。
  • ストリームはチャンクの型制約に合わせるため帯域内に期限を埋め込まず、記録だけで管理します。encodeStream と decodeStream はデータを透過します。
  • TTL の解決は ttl 変数を優先し、次に expiresAt 変数、最後に defaultTtl を使います。名前空間付き変数が非名前空間名より優先されます。

記録の確定タイミング

このミドルウェアはストレージの成否を afterUse で受け取り、記録を確定します。use・encode・encodeStream の時点では記録を変更せず、操作内の変数に確定待ちの値を保存します。そのため、書き込みに失敗したときに、存在しない値へ期限だけが付くことはありません。afterUse に渡される宛先は、このミドルウェアが適用された分岐のものだけです。

  • set はすべての宛先が確定したときに、保存した期限を記録へ反映します。TTL なしの上書きでは記録を消します。失敗したときは以前の記録を残します。
  • copy は、このミドルウェアが適用された分岐で TTL の対象となるすべての宛先が committed になったときに、コピー元の期限をコピー先へ引き継ぎます。コピー元に記録がなければコピー先の記録を消します。一部でも notFound や failed がある場合は、コピー先の記録を変更しません。
  • move は、このミドルウェアが適用された分岐で TTL の対象となるすべての宛先が committed になったときに、移動先の記録を更新して移動元の記録を消します。一部でも notFound や failed がある場合は、移動元の記録を残し、移動先の記録は変更しません。
  • ストレージの確定後に操作が中断された場合でも、このミドルウェアが適用された分岐で TTL の対象となるすべての宛先が committed なら、その結果を記録へ反映します。未確定の宛先があれば、失敗時と同じく記録を変更しません。
  • コピー元・移動元の期限は use の時点で固定せず、転送完了後の afterUse (キーロック保持中) に記録から読み直します。use の後に並行する set が期限を更新した場合も、その現在の期限を引き継ぎます。
  • delete と clear は成功したときだけ記録を消します。部分成功では残っている値の期限を失わないよう、記録を残します。
  • 期限切れのコピー元・移動元は KeyNotFoundError になりますが、記録は残します。ストリームは帯域内に期限を持たないため、失敗したコピー・移動の後でも失効を判定できます。成功した set は記録を新しい期限で置き換え (TTL なしなら削除し)、成功した delete・clear は記録を消します。
  • decodeStream が期限切れを検出しても記録は残します。同じキーへの decodeStream は引き続き KeyNotFoundError になります。
  • get・stream の repair による書き戻しは読み取りの一部として扱われ、afterUse は呼ばれません。そのため、書き戻し先のストレージには期限エンベロープを持たないストリームの記録が読み取り側のインスタンスに確定されません。

エラー

このパッケージが投げるエラーは次のとおりです。

TtlInvalidValueError

プロパティ型
actualunknown

指定された TTL の値です。

型unknown

意味

TTL 値が相対秒数・絶対時刻・オブジェクト形式のいずれとしても解釈できないときに、このエラーが投げられます。コンストラクターの defaultTtl と書き込みごとの変数の両方が検証対象です。

対処方法

有限の数値 (相対秒数)、Date、または { ttl } や { expiresAt } の形のオブジェクトを指定してください。文字列や非有限値は受け付けません。

例

以下は、TtlInvalidValueError を捕捉する例です。

import { TtlInvalidValueError } from "@unikvs/ttl";

try {
  await kvs.set("report", "hello", { vars: { ttl: "60" } });
} catch (ex) {
  if (ex instanceof TtlInvalidValueError) {
    console.log(ex.meta); // { actual: "60" }
  } else {
    throw ex;
  }
}

期限切れの読み取りは @unikvs/core の KeyNotFoundError として失敗します。詳細はエラーを参照してください。

このページは役に立ちましたか?