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

ストレージ

@unikvs/core の IStorage。保存先プラグインが実装するインターフェース、型引数、必須と任意のメンバー、実装例を説明します。

概要

IStorage は保存先プラグインが実装するインターフェースです。name と isOpen、CRUD 操作を持ち、ライフサイクル、copy・move、ストリーム取得は任意です。4 つの型引数でデータとチャンクの型を調整します。

import type { IStorage } from "@unikvs/core";

type BinaryStorage = IStorage<Uint8Array>;
type ObjectStorage = IStorage<{ readonly id: number }>;
type FullStorage = IStorage<Uint8Array, string, ArrayBuffer, number>;

型引数

型引数 既定値 対応する操作
TWriteData any write(c, data) の data
TReadData TWriteData read(c) の戻り値
TWriteChunk any getWritable(c) のチャンク
TReadChunk TWriteChunk getReadable(c) のチャンク

TReadData の既定値は TWriteData、TReadChunk の既定値は TWriteChunk です。読み書きで型が変わらない場合はデータの型引数 (TWriteData) のみを指定し、ストリームを使わない場合はチャンクの型引数を省略できます。

// 読み取りは文字列、書き込みはバイト列
type TextReadStorage = IStorage<Uint8Array, string>;

// チャンクまで個別に指定
type ChunkedStorage = IStorage<Uint8Array, string, ArrayBuffer, number>;

チャンク型とストリーム型の関係はストリームを参照してください。

基本プロパティー

メンバー 必須 説明
name 必須 プラグイン名
isOpen 必須 初期化済みかどうか

open(c) と close(c) は任意です。必要な初期化や後始末がある場合のみ実装します。呼び出し側は isOpen で状態を確認します。

CRUD

メンバー 必須 シグネチャ 説明
write 必須 (c: WriteContext, data: TWriteData) => MaybePromise<void> データを保存する
read 必須 (c: ReadContext) => MaybePromise<TReadData> データを取得する
exists 必須 (c: ExistsContext) => MaybePromise<boolean> キーの有無を返す
delete 必須 (c: DeleteContext) => MaybePromise<void> キーを削除する
clear 必須 (c: ClearContext) => MaybePromise<void> すべてのデータを削除する

すべてのメソッドは MaybePromise (T | PromiseLike<T>) を返すため、同期実装も非同期実装も可能です。

コピーと移動

メンバー 必須 シグネチャ 説明
copy 任意 (c: CopyContext) => MaybePromise<void> キーを別のキーへ複製する
move 任意 (c: MoveContext) => MaybePromise<void> キーを別のキーへ移動する

CopyContext と MoveContext は、コピー元・移動元のキーを key、コピー先・移動先のキーを destinationKey に持ちます。コピー先・移動先に既存の値がある場合は上書きします。

move は移動先への書き込みが成功した後に移動元を削除します。移動先への書き込みに失敗した場合は移動元を残してください。コピー元・移動元が存在しない場合は KeyNotFoundError を投げます。key と destinationKey が同じ場合は、存在確認だけを行い、値を削除しません。

copy または move を省略した場合、UniKvs 内部の UniKvsStorage が read・write・delete を組み合わせて同じ動作を再現します。ストレージ固有の効率的な API がある場合だけ実装します。

ストリーム取得

メンバー 必須 シグネチャ 説明
getWritable 任意 (c: GetWritableContext) => MaybePromise<IWritableStream<TWriteChunk>> 書き込み用ストリームを取得する
getReadable 任意 (c: GetReadableContext) => MaybePromise<IReadableStream<TReadChunk>> 読み取り用ストリームを取得する

ストリームに対応しないストレージは実装を省略します。対応宣言は型ではなく実行時の有無で判定されるため、未対応の操作が呼ばれた場合は各プラグインが適切なエラーを投げます。

getWritable のコンテキストは ISetContext と同じ形状で、getReadable のコンテキストは IStreamContext と同じ形状です (コンテキストの別名)。開始前に中断済みのシグナルで呼ばれた場合は StorageAbortedError で即座に拒否します。

他ストレージの失敗

複数のストレージへ並列に書き込む際、他の保存先で失敗があっても各ストレージに通知されます。

メンバー 必須 説明
onOtherWriteError 任意 複数ストレージ書き込み時に他ストレージの失敗を受け取る

引数は (c: OtherWriteErrorHandlerContext, errors: readonly IOtherWriteError[]) です。OtherWriteErrorHandlerContext は WriteContext | GetWritableContext で、書き込み系のどちらの経路で失敗したかを示します。

IOtherWriteError の形状は次のとおりです。

プロパティ型
plugin"storage"

固定値です。

型"storage"
errorsreadonly { plugin: "storage", reason: unknown }[]

他ストレージごとの失敗理由です。

型readonly { plugin: "storage", reason: unknown }[]

コンテキストの別名

IStorage 名前空間は パイプラインコンテキスト の別名を提供します。プラグイン側は IStorage.* を使うと意図が明確になります。

別名 実体 用途
IStorage.OpenContext IOpenContext open
IStorage.CloseContext ICloseContext close
IStorage.WriteContext ISetContext | ICopyContext | IMoveContext write
IStorage.GetWritableContext ISetContext getWritable
IStorage.ReadContext IGetContext | ICopyContext | IMoveContext read
IStorage.GetReadableContext IStreamContext getReadable
IStorage.ExistsContext IHasContext exists
IStorage.DeleteContext IDeleteContext | ICopyContext | IMoveContext delete
IStorage.CopyContext ICopyContext | IMoveContext copy
IStorage.MoveContext IMoveContext move
IStorage.ClearContext IClearContext clear
IStorage.OtherWriteErrorHandlerContext WriteContext | GetWritableContext onOtherWriteError

copy / move のフォールバックでは、read・write・delete に元の操作種別 ("copy" または "move") と、呼び出しに応じたキーを持つコンテキストが渡されます。そのため ReadContext・WriteContext・DeleteContext には copy/move コンテキストも含まれます。

実装例

import {
  KeyNotFoundError,
  type IStorage,
} from "@unikvs/core";

export class Memory implements IStorage<string> {
  public readonly name = "Memory";
  public isOpen = false;
  private readonly data = new Map<string, string>();

  public open(): void {
    this.isOpen = true;
  }

  public close(): void {
    this.isOpen = false;
  }

  public write(c: IStorage.WriteContext, value: string): void {
    this.data.set(c.key, value);
  }

  public read(c: IStorage.ReadContext): string {
    const value = this.data.get(c.key);
    if (value === undefined) {
      throw new KeyNotFoundError({ key: c.key });
    }
    return value;
  }

  public exists(c: IStorage.ExistsContext): boolean {
    return this.data.has(c.key);
  }

  public delete(c: IStorage.DeleteContext): void {
    this.data.delete(c.key);
  }

  public copy(c: IStorage.CopyContext): void {
    const value = this.data.get(c.key);
    if (value === undefined) {
      throw new KeyNotFoundError({ key: c.key });
    }
    if (c.key !== c.destinationKey) {
      this.data.set(c.destinationKey, value);
    }
  }

  public move(c: IStorage.MoveContext): void {
    this.copy(c as unknown as IStorage.CopyContext);
    if (c.key !== c.destinationKey) {
      this.data.delete(c.key);
    }
  }

  public clear(): void {
    this.data.clear();
  }
}
import {
  KeyNotFoundError,
  type IStorage,
} from "@unikvs/core";

export class AsyncMemory implements IStorage<string> {
  public readonly name = "AsyncMemory";
  public isOpen = false;
  private readonly data = new Map<string, string>();

  public async open(): Promise<void> {
    this.isOpen = true;
  }

  public async close(): Promise<void> {
    this.isOpen = false;
  }

  public async write(c: IStorage.WriteContext, value: string): Promise<void> {
    this.data.set(c.key, value);
  }

  public async read(c: IStorage.ReadContext): Promise<string> {
    const value = this.data.get(c.key);
    if (value === undefined) {
      throw new KeyNotFoundError({ key: c.key });
    }
    return value;
  }

  public async exists(c: IStorage.ExistsContext): Promise<boolean> {
    return this.data.has(c.key);
  }

  public async delete(c: IStorage.DeleteContext): Promise<void> {
    this.data.delete(c.key);
  }

  public async copy(c: IStorage.CopyContext): Promise<void> {
    const value = this.data.get(c.key);
    if (value === undefined) {
      throw new KeyNotFoundError({ key: c.key });
    }
    if (c.key !== c.destinationKey) {
      this.data.set(c.destinationKey, value);
    }
  }

  public async move(c: IStorage.MoveContext): Promise<void> {
    await this.copy(c as unknown as IStorage.CopyContext);
    if (c.key !== c.destinationKey) {
      this.data.delete(c.key);
    }
  }

  public async clear(): Promise<void> {
    this.data.clear();
  }
}

実装時の要点は次のとおりです。

型引数を決める

保存する値の型を IStorage<T> に指定します。読み書きで型が変わる場合やストリームを使う場合は、4 つすべてを指定します。

必須メンバーを実装する

name・isOpen と CRUD の 5 メソッドは必須です。read では見つからないキーに KeyNotFoundError を投げます。copy・move は任意で、省略時は read・write・delete によるフォールバックが使われます。

任意メンバーを足す

初期化が必要なら open・close、ストリーム対応なら getWritable・getReadable、他ストレージの失敗通知が要れば onOtherWriteError を実装します。

API リファレンス

IStorage

保存先プラグインが実装するインターフェースです。4 つの型引数でデータとチャンクの型を調整します。

プロパティ型
namestring

プラグイン名です。

型string
isOpenboolean

初期化済みかどうかを示します。

型boolean
open?(c: IStorage.OpenContext) => MaybePromise<void>

初期化処理を行います。

型(c: IStorage.OpenContext) => MaybePromise<void>
close?(c: IStorage.CloseContext) => MaybePromise<void>

終了処理を行います。

型(c: IStorage.CloseContext) => MaybePromise<void>
write(c: IStorage.WriteContext, data: TWriteData) => MaybePromise<void>

データを保存します。

型(c: IStorage.WriteContext, data: TWriteData) => MaybePromise<void>
read(c: IStorage.ReadContext) => MaybePromise<TReadData>

データを取得します。なければ `KeyNotFoundError` を投げます。

型(c: IStorage.ReadContext) => MaybePromise<TReadData>
exists(c: IStorage.ExistsContext) => MaybePromise<boolean>

キーの有無を返します。

型(c: IStorage.ExistsContext) => MaybePromise<boolean>
delete(c: IStorage.DeleteContext) => MaybePromise<void>

キーを削除します。

型(c: IStorage.DeleteContext) => MaybePromise<void>
clear(c: IStorage.ClearContext) => MaybePromise<void>

すべてのデータを削除します。

型(c: IStorage.ClearContext) => MaybePromise<void>
copy?(c: IStorage.CopyContext) => MaybePromise<void>

キーの値を別のキーへ複製します。省略時は read/write で再現されます。

型(c: IStorage.CopyContext) => MaybePromise<void>
move?(c: IStorage.MoveContext) => MaybePromise<void>

キーの値を別のキーへ移動します。省略時は copy/delete で再現されます。

型(c: IStorage.MoveContext) => MaybePromise<void>
onOtherWriteError?(c: IStorage.OtherWriteErrorHandlerContext, errors: readonly IStorage.IOtherWriteError[]) => MaybePromise<void>

他ストレージの失敗を受け取ります。

型(c: IStorage.OtherWriteErrorHandlerContext, errors: readonly IStorage.IOtherWriteError[]) => MaybePromise<void>

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