Upgrade to Pro — share decks privately, control downloads, hide ads and more …

deno-redisの紹介とJSRパッケージの運用について (toranoana.deno #21)

deno-redisの紹介とJSRパッケージの運用について (toranoana.deno #21)

Avatar for uki00a

uki00a

June 18, 2025
Tweet

Other Decks in Programming

Transcript

  1. deno-redis ( jsr:@db/redis ) について Deno で実装されたRedis クライアント JSR (

    jsr:@db/redis ) と deno.land/x (https://deno.land/x/redis) で 公開 作者はkeroxp さんです 2020 年ぐらいからdenodrivers というコミュニティ内で細々とメン テナンスを続けています
  2. deno-redis ( jsr:@db/redis ) について import { connect } from

    "jsr:@db/redis"; const redis = await connect({ hostname: "127.0.0.1", port: 6379, }); const ok = await redis.set("key", "foo"); const result = await redis.get("key");
  3. Deno v1 におけるIO ( Deno.{Reader,Writer} ) // Deno.Reader const buf

    = new Uint8Array(128); doSomethingWithInput(await conn.read(buf)); // Deno.Writer const data = new TextEncoder().encode("foo"); await conn.write(data);
  4. Deno v2 におけるIO (Stream API) // ReadableStream const reader =

    conn.readable.getReader(); doSomethingWithInput(await reader.read()); reader.releaseLock(); // WritableStream const writer = conn.writable.getWriter(); const data = new TextEncoder().encode("foo"); await writer.write(data); writer.releaseLock();
  5. Deno.{Reader,Writer} からStream API への移行 deno-redis においても Deno.Reader 及び Deno.Writer からStream

    API への移行対応を実施することに しかし、移行にあたってビッグバンリリースはできれば避けたいで す...
  6. Deno.{Reader,Writer} からStream API への移行 段階的に移行するために、 Protocol という抽象を用意することにしま した。 export interface

    Protocol { sendCommand( command: string, args: Array<RedisValue>, returnsUint8Arrays?: boolean, ): Promise<RedisReply>; readReply(returnsUint8Array?: boolean): Promise<RedisReply>; writeCommand(command: Command): Promise<void>; // ... }
  7. Deno.{Reader,Writer} からStream API への移行 // `Deno.Reader` & `Deno.Writer` ベースの実装 (Deno

    v1 向け) export class DenoStreamBasedProtocol implements Protocol { #reader: BufReader; #writer: BufWriter; constructor(conn: Deno.Conn) { this.#reader = new BufReader(conn); this.#writer = new BufWriter(conn); } // 実装... } // Stream API ベースの実装 (Deno v2 向け) export class WebStreamBasedProtocol implements Protocol { #readable: BufferedReadableStream; #writable: WritableStream<Uint8Array>; constructor(conn: Deno.Conn) { this.#readable = new BufferedReadableStream(conn.readable); this.#writable = conn.writable; } // 実装... }
  8. Deno.{Reader,Writer} からStream API への移行 ユーザーが任意でそれぞれの実装を切り替えられるようにします: // Deno v1 向け (Deno.Reader

    & Deno.Writer) import { connect as connectV1 } from "jsr:@db/redis"; // Deno v2 向け (Stream API) import { connect as connectV2 } from "jsr:@db/redis/experimental/web-streams-connection"
  9. Deno.{Reader,Writer} からStream API への移行 互換性を担保するために、それぞれの実装に対して同一のテストを実 行します Deno.test("commands", async (t) =>

    { for (const [type, connector] of [["v1", connectV1], ["v2", connectV2]]) { await t.step(type, async (t) => { await t.step("SET", async () => { using client = await connector(); assertEquals(await client.set("key", "foo"), "OK"); }); }); } });
  10. Stream API への移行に伴うパフォーマンスの劣化につ いて なんとかStream API への対応については実施できました しかし、Stream API ベースの実装は

    Deno.Reader & Deno.Writer ベー スの実装と比較してパフォーマンスが悪いことが分かりました パフォーマンス改善による効果を計測できるよう、ベンチマークの仕 組みがあると便利です
  11. ベンチマーク Deno には deno bench というベンチマークのための仕組みあります const value = "bar".repeat(10);

    Deno.bench("get & set", async (b) => { const client = await connect(options); b.start(); const key = "foo"; await client.set(key, value); await client.get(key); b.end(); });
  12. ベンチマーク やりたいこと Deno ( deno-redis ) とNode.js ( ioredis )

    で同じベンチマークコード を実行したい 課題 deno bench はNode.js では動作しません...
  13. ベンチマーク benny はDeno とNode.js の両方で動きます (benchmark/benchmark.js) import { add, suite,

    /* ... */ } from "benny"; export const run = ({ driver, // "deno-redis" or "ioredis" client, // `Redis` object of `deno-redis` or `ioredis` }) => suite(driver, add("set & get", () => { return async () => { await client.set("foo", "bar"); await client.get("foo"); }; }), );
  14. 解決策 BYOB (ReadableStreamBYOBReader) を使用することでパフォーマ ンスをかなり改善できることが判明しました ( Deno.Reader & Deno.Writer と同等程度のパフォーマンスを発揮してくれる)

    // ReadableStreamBYOBReader を使いたい場合、`mode: "byob"` を指定する readableStream.getReader({ mode: "byob" }); バッファリングの仕組みが欲しかったので、自前で用意しています (BufferedReadableStream)
  15. おすすめの設定 JSR パッケージを開発する際は no-console ルールを無効化しておく とおすすめです ( パッケージ内に意図せぬデバッグ用のログが残っ てしまうことを防止できます) {

    "lint": { "rules": { "include": ["no-console"] }, "plugins": ["jsr:@uki00a/[email protected]"] } } Deno v2.2 でプラグインシステムがサポートされました (deno-lint- plugin-extra-rules という自作プラグインを使っています)
  16. 依存パッケージをキャッシュする denoland/[email protected] から依存パッケージのキャッシュがサポ ートされています: jobs: test: runs-on: ubuntu-latest steps: -

    uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Deno uses: denoland/setup-deno@e95548e56dfa95d4e1a28d6f422fafe75c4c26fb # v2.0.3 with: deno-version: 2.3.3 cache: true # キャッシュを有効化 cache-hash: ${{ hashFiles('.deno-version', 'deno.lock') }}
  17. Deno Deploy Early Access とは? Databases: Coming soon Logs: Supported

    Tracing: Supported Metrics: Supported OpenTelemetry export: Work in progress 公式ドキュメントより引用
  18. まとめ. Deno ライブラリの開発で意識すると良 いこと 1. ベンチマークやLint 、テストなどのCI 周りを整備すると、メンテナ ンスが継続しやすくなると思います 2.

    大掛かりな移行や改修をする場合、抽象の導入やベンチマークによ る計測などを活用して段階的に移行すると安全だと思います 3. Deno Deploy Early Access によってOpenTelemetry などの重要性が より増すかもしれないと思っています