---
title: "@unikvs/etcd"
description: "etcd にバイト列を保存する @unikvs/etcd の使い方を説明します。"
---

## 概要 [#overview]

`@unikvs/etcd` は etcd にバイト列を保存するストレージプラグインです。設定の共有やサービスディスカバリーなど、分散協調が必要な場合に使います。

- `name` は `"Etcd"` で、`isOpen` で開閉状態を確認します。
- `open()` で接続し、使い終わったら `close()` で解放します。
- このストレージは、キーに名前空間と接頭辞（既定では `"unikvs:"`）を付与し、`clear()` の削除範囲を限定します。
- 接続先を省略した場合は、`ETCD_ENDPOINTS` 環境変数（カンマ区切り）を使い、未設定のときは `"http://127.0.0.1:2379"` に接続します。
- `leaseTtlSeconds` を指定すると、書き込んだキーにリースの有効期限（秒）を付与します。

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

```package-install
npm install @unikvs/etcd
```

別途 etcd クラスターが必要です。

## 使い方 [#usage]

Etcd は IStorage を実装しています。UniKvs には persist() で渡します。

```ts
import { Etcd } from "@unikvs/etcd";
```

### Etcd [#etcd]

etcd に保存するストレージです。利用者は `open()` で開き、使い終わったら `close()` で閉じます。

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

| Prop | Type | Default | Description |
| - | - | - | - |
| `options?` | `[#etcd-options] EtcdStorageOptions` | - | 接続先と本パッケージの動作を設定するオプションです。 |

### EtcdStorageOptions [#etcdstorageoptions]

etcd ストレージのオプションです。

| Prop | Type | Default | Description |
| - | - | - | - |
| `client?` | `[#etcdstorageoptions-client] EtcdClientLike` | - | 注入する etcd クライアントです。指定時は接続を行わず、所有権は呼び出し側に残ります。 |
| `endpoints?` | `[#etcdstorageoptions-endpoints] string \| readonly string[]` | - | 接続先の etcd エンドポイントです。`client` 指定時は使われません。 |
| `keyPrefix?` | `[#etcdstorageoptions-keyprefix] string` | - | すべてのキーに付与する接頭辞です。既定値は `"unikvs:"` です。 |
| `leaseTtlSeconds?` | `[#etcdstorageoptions-leasettlseconds] number` | - | すべての書き込みに付与するリースの有効期限 (秒) です。自動失効させたい場合に使います。 |
| `namespace?` | `[#etcdstorageoptions-namespace] string` | - | `keyPrefix` の前に付与する名前空間です。既定値は `""` です。 |

### EtcdPutOptions [#etcdputoptions]

`put` に渡せる etcd 固有のオプションです。

| Prop | Type | Default | Description |
| - | - | - | - |
| `leaseTtlSeconds?` | `[#etcdputoptions-leasettlseconds] number` | - | キーに付与するリースの有効期限 (秒) です。 |

### EtcdGetAllResult [#etcdgetallresult]

`getAll` が返すキーと値の組の形式です。レコード、マップ、エントリー反復可能オブジェクトのいずれかです。

### EtcdClientLike [#etcdclientlike]

etcd クライアントの最小インターフェースです。テストではこのインターフェースを満たすフェイクを注入し、実環境では `endpoints` から組み立てた etcd3 クライアントを使います。

### NativeEtcdClientLike [#nativeetcdclientlike]

etcd3 パッケージのクライアントのうち、本パッケージが使う最小部分です。

### wrapNativeEtcdClient [#wrapnativeetcdclient]

etcd3 のネイティブクライアントを `EtcdClientLike` に適合させます。`leaseTtlSeconds` 指定時はリース付きで書き込みます。

以下は、値を保存して読み戻す例です。

```ts
import { Etcd } from "@unikvs/etcd";
import * as unikvs from "unikvs";

const kvs = new unikvs.Builder<{ file: unikvs.Value<Uint8Array> }>()
  .persist(new Etcd({ endpoints: "http://127.0.0.1:2379" }))
  .build();

await kvs.open();

await kvs.set("file", new Uint8Array([0, 1, 2]));

const data = await kvs.get("file");
console.log(data); // Uint8Array(3) [0, 1, 2]

await kvs.close();
```

## データ型 [#data-types]

| 操作                                    | 対応するデータ型          |
| --------------------------------------- | ------------------------- |
| `write`・`read`                         | `Uint8Array<ArrayBuffer>` |
| `getWritable`・`getReadable` のチャンク | `Uint8Array<ArrayBuffer>` |

- `write`・`read` にはバイト列 (`Uint8Array<ArrayBuffer>`) を使います。
- ストリームの読み書きに対応し、チャンクは `Uint8Array<ArrayBuffer>` です。
- 書き込みストリームはチャンクを蓄積し、完了時に単発の書き込みで保存します。
- 読み取りストリームは取得したバイト列を単一のチャンクとして送出します。

## エラー [#errors]

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

### InvalidLeaseTtlError [#error-invalid-lease-ttl]

コンストラクターの `leaseTtlSeconds` が正の整数ではないときに投げます。

| Prop | Type | Default | Description |
| - | - | - | - |
| `actual` | `[#invalid-lease-ttl-actual] unknown` | - | 受け取った `leaseTtlSeconds` の値です。 |

#### 意味

`leaseTtlSeconds` に正の整数 (秒) 以外が指定されました。

#### 対処方法

`leaseTtlSeconds` に正の整数 (秒) を指定してください。

### InvalidEndpointsError [#error-invalid-endpoints]

コンストラクターの `endpoints` が空の文字列や空の配列のときに投げます。

| Prop | Type | Default | Description |
| - | - | - | - |
| `actual` | `[#invalid-endpoints-actual] unknown` | - | 受け取った `endpoints` の値です。 |

#### 意味

`endpoints` に空の文字列、空の配列、または空文字を含む配列が指定されました。

#### 対処方法

`endpoints` に空でない文字列か、空でない文字列だけを含む空でない配列を指定してください。

### KeyNotFoundError [#error-key-not-found]

`@unikvs/core` の共通エラーです。存在しないキーを読み取ったときに投げます。

#### 意味

指定したキーが存在しません。`@unikvs/core` の共通エラーです。

#### 対処方法

`exists` で確認してから読み出してください。

#### 例

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

```ts
import { KeyNotFoundError } from "@unikvs/etcd";

try {
  const data = await kvs.get("missing");
} catch (ex) {
  if (ex instanceof KeyNotFoundError) {
    console.log("キーが見つかりません");
  } else {
    throw ex;
  }
}
```
