サーバーレスでグローバルに分散された時系列APIをTimescaleで作成する
このチュートリアルでは、Timescale ↗(彼らはPostgreSQLをクラウドで高速化します)に保存された時系列データを取り込み、クエリするAPIをWorkers上に構築する方法を学びます。
データを取り込むためのAPIルートを公開するWorker関数を作成してデプロイし、Hyperdrive ↗を使用してエッジからデータベース接続をプロキシし、新しいデータベース接続をリクエストごとに作成しないように接続プールを維持します。
以下のことを学びます:
- Cloudflare Workerを構築してデプロイする。
- Wrangler CLIを使用してWorkerシークレットを管理する。
- Timescaleデータベースサービスをデプロイする。
- Hyperdriveを使用してWorkerをTimescaleデータベースサービスに接続する。
- 新しいAPIをクエリする。
Timescaleについての詳細は、彼らのドキュメント ↗を読むことで学べます。
以下のコマンドを実行して、コマンドラインからWorkerプロジェクトを作成します:
npm create cloudflare@latest -- timescale-apiyarn create cloudflare@latest timescale-apipnpm create cloudflare@latest timescale-apiFor setup, select the following options:
- For What would you like to start with?, choose
Hello Worldの例. - For Which template would you like to use?, choose
Hello World Worker. - For Which language do you want to use?, choose
TypeScript. - For Do you want to use git for version control?, choose
Yes. - For Do you want to deploy your application?, choose
No(we will be making some changes before deploying).
アプリケーションがデプロイされたURLをメモしておいてください。これはGitHubウェブフックを設定する際に使用します。
作成したWorkerプロジェクトのディレクトリに移動します:
cd timescale-api新しいサービスを作成する場合は、Timescale Console ↗にアクセスし、以下の手順に従ってください:
- 右上の黒いプラスを選択してサービスを作成を選択します。
- サービスの種類として時系列を選択します。
- 希望するリージョンとインスタンスサイズを選択します。このチュートリアルには1 CPUで十分です。
- ランダムに生成されたサービス名を置き換えるためのサービス名を設定します。
- サービスを作成を選択します。
- 右側の接続情報ダイアログを展開し、サービスURLをコピーします。
- 表示されたパスワードをコピーします。これは再取得できません。
- パスワードを保存しました、サービスの概要に移動を選択します。
以前に作成したサービスを使用している場合は、Timescale Console ↗でサービス接続情報を取得できます:
- Hyperdriveが接続するサービス(データベース)を選択します。
- 接続情報を展開します。
- サービスURLをコピーします。サービスURLはHyperdriveが接続するために使用する接続文字列です。この文字列にはデータベースのホスト名、ポート番号、データベース名が含まれています。
以下のようにサービスURLにパスワードを挿入します(@の後の部分はそのままにします):
postgres://tsdbadmin:YOURPASSWORD@...これは以下のセクションでSERVICEURLと呼ばれます。
Timescaleでは、通常のPostgreSQLテーブルをハイパーテーブル ↗に変換できます。ハイパーテーブルは、時系列、イベント、または分析データを扱うために使用されます。この変更を行うと、Timescaleはハイパーテーブルのパーティショニングをシームレスに管理し、圧縮や継続的集計などの他の機能を適用できるようにします。
前のステップでコピーしたサービスURLを使用してTimescaleデータベースに接続します(パスワードが埋め込まれています)。
デフォルトのPostgreSQL CLIツールpsql ↗を使用して接続する場合、以下のようにpsqlを実行します(前のステップからのサービスURLを置き換えます)。PgAdmin ↗のようなグラフィカルツールを使用して接続することもできます。
psql <SERVICEURL>接続したら、以下のSQLを貼り付けてテーブルを作成します:
CREATE TABLE readings( ts timestamptz DEFAULT now() NOT NULL, sensor UUID NOT NULL, metadata jsonb, value numeric NOT NULL );
SELECT create_hypertable('readings', 'ts');Timescaleは、データを取り込み、クエリする際に残りの管理を行います。
新しいHyperdriveインスタンスを作成するには、以下が必要です:
- ステップ2からのSERVICEURL。
- Hyperdriveサービスの名前。今回のチュートリアルではhyperdriveを使用します。
Hyperdriveは、createコマンドと--connection-string引数を使用してこの情報を渡します。以下のように実行します:
npx wrangler hyperdrive create hyperdrive --connection-string="SERVICEURL"このコマンドは、Hyperdrive IDを出力します。次に、以下の内容でwrangler.toml構成にHyperdrive構成をバインドできます:
name = "timescale-api"main = "src/index.ts"compatibility_date = "2024-08-21"compatibility_flags = [ "nodejs_compat_v2"]
[[hyperdrive]]binding = "HYPERDRIVE"id = "your-id-here"WorkerプロジェクトにPostgresドライバーをインストールします:
npm install pg次に、以下のWorkerコードをコピーし、./src/index.tsの現在のコードを置き換えます。以下のコードは:
- Hyperdriveを使用して、
env.HYPERDRIVE.connectionStringから直接ドライバーに接続します。 - Timescaleに一度のトランザクションで挿入するためのJSON読み取りの配列を受け入れる
POSTルートを作成します。 limitパラメータを受け取り、最新の読み取りを返すGETルートを作成します。これはIDやタイムスタンプでフィルタリングするように適応できます。
import { Client } from "pg";
export interface Env { HYPERDRIVE: Hyperdrive;}
export default { async fetch(request, env, ctx): Promise<Response> { const client = new Client({ connectionString: env.HYPERDRIVE.connectionString, }); await client.connect();
const url = new URL(request.url); // JSONを読み取りとして挿入するためのルートを作成 if (request.method === "POST" && url.pathname === "/readings") { // リクエストのJSONペイロードを解析 const productData = await request.json();
// 生のクエリを書く。jsonb_to_recordsetを使用してJSONをPG INSERT形式に展開し、 // tsフィールドが存在しない場合は現在のタイムスタンプで挿入するためにcoalesceを使用 const insertQuery = ` INSERT INTO readings (ts, sensor, metadata, value) SELECT coalesce(ts, now()), sensor, metadata, value FROM jsonb_to_recordset($1::jsonb) AS t(ts timestamptz, sensor UUID, metadata jsonb, value numeric) `;
const insertResult = await client.query(insertQuery, [ JSON.stringify(productData), ]);
// 挿入された生の行数を収集して返す const resp = new Response(JSON.stringify(insertResult.rowCount), { headers: { "Content-Type": "application/json" }, });
ctx.waitUntil(client.end()); return resp;
// 時間枠内でクエリするためのルートを作成 } else if (request.method === "GET" && url.pathname === "/readings") { const limit = url.searchParams.get("limit");
// 渡されたlimitパラメータを使用して読み取りテーブルをクエリ const result = await client.query( "SELECT * FROM readings ORDER BY ts DESC LIMIT $1", [limit], );
// 結果をJSONとして返す const resp = new Response(JSON.stringify(result.rows), { headers: { "Content-Type": "application/json" }, });
ctx.waitUntil(client.end()); return resp; } },} satisfies ExportedHandler<Env>;以下のコマンドを実行してWorkerを再デプロイします:
npx wrangler deployアプリケーションは現在ライブで、timescale-api.<YOUR_SUBDOMAIN>.workers.devでアクセス可能です。正確なURIは、実行したwranglerコマンドの出力に表示されます。
デプロイ後、Cloudflare Workerを使用してTimescale IoT読み取りデータベースと対話できます。エッジからの接続は、Cloudflare Hyperdriveを使用して接続するため、より高速になります。
これで、Cloudflare Workerを使用してreadingsテーブルに新しい行を挿入できます。この機能をテストするには、WorkerのURLに/readingsパスを付けて、JSONペイロードに新しい製品データを含むPOSTリクエストを送信します:
[ { "sensor": "6f3e43a4-d1c1-4cb6-b928-0ac0efaf84a5", "value": 0.3 }, { "sensor": "d538f9fa-f6de-46e5-9fa2-d7ee9a0f0a68", "value": 10.8 }, { "sensor": "5cb674a0-460d-4c80-8113-28927f658f5f", "value": 18.8 }, { "sensor": "03307bae-d5b8-42ad-8f17-1c810e0fbe63", "value": 20.0 }, { "sensor": "64494acc-4aa5-413c-bd09-2e5b3ece8ad7", "value": 13.1 }, { "sensor": "0a361f03-d7ec-4e61-822f-2857b52b74b3", "value": 1.1 }, { "sensor": "50f91cdc-fd19-40d2-b2b0-c90db3394981", "value": 10.3 }]このチュートリアルでは、ts(タイムスタンプ)とmetadata(JSONブロブ)は省略されるため、それぞれnow()とNULLに設定されます。
POSTリクエストを送信した後、WorkerのURLに/readingsパスを付けてGETリクエストを発行することもできます。limitパラメータを設定して、返されるレコードの量を制御します。
curlがインストールされている場合は、以下のコマンドでテストできます(<YOUR_SUBDOMAIN>を上記のデプロイコマンドからのサブドメインに置き換えます):
curl --request POST --data @- 'https://timescale-api.<YOUR_SUBDOMAIN>.workers.dev/readings' <<EOF[ { "sensor": "6f3e43a4-d1c1-4cb6-b928-0ac0efaf84a5", "value":0.3}, { "sensor": "d538f9fa-f6de-46e5-9fa2-d7ee9a0f0a68", "value":10.8}, { "sensor": "5cb674a0-460d-4c80-8113-28927f658f5f", "value":18.8}, { "sensor": "03307bae-d5b8-42ad-8f17-1c810e0fbe63", "value":20.0}, { "sensor": "64494acc-4aa5-413c-bd09-2e5b3ece8ad7", "value":13.1}, { "sensor": "0a361f03-d7ec-4e61-822f-2857b52b74b3", "value":1.1}, { "sensor": "50f91cdc-fd19-40d2-b2b0-c90db3394981", "metadata": {"color": "blue" }, "value":10.3}]EOFcurl "https://timescale-api.<YOUR_SUBDOMAIN>.workers.dev/readings?limit=10"このチュートリアルでは、Timescale、Workers、Hyperdrive、TypeScriptを使用してエッジから読み取りを取り込み、クエリする作業例を作成する方法を学びました。
- Hyperdriveの仕組みについてさらに学ぶ。
- Timescale ↗についてさらに学ぶ。
- 一般的な問題をデバッグするためにトラブルシューティングガイドを参照する。