google-agents-cli-scaffold
Project scaffolding, deployment configuration, and CI/CD setup for Google ADK agents.
couchdb-client
Obsidian LiveSync の CouchDbClient の構造と使用方法を説明します。CouchDbRepository トレイトの実装方法、HTTP プロキシパターン(forward_request)、longpoll リクエストの処理、メトリクス収集、ヘルスチェックの実装を理解・拡張する際に使用します。CouchDB 関連の機能追加、トラブルシューティング、パフォーマンス改善を依頼されたときに使用してください。
Full skill instructions
このスキルは、Obsidian LiveSync の CouchDbClient の構造と使用方法を説明します。CouchDbClient は CouchDB との通信を担当し、HTTP プロキシとして Obsidian LiveSync プラグインからのリクエストを中継します。
domain/services.rs → CouchDbRepository トレイト(抽象)
infrastructure/couchdb.rs → CouchDbClient(実装)
pub struct CouchDbClient {
client: Client, // reqwest HTTP クライアント
base_url: String, // CouchDB ベース URL
username: String, // 認証ユーザー名
password: String, // 認証パスワード
}
初期化:
let client = CouchDbClient::new(
"http://couchdb:5984/",
"admin",
"password",
);
トレイト定義(domain/services.rs):
#[async_trait]
pub trait CouchDbRepository {
// ドキュメント操作
async fn get_document(&self, db_name: &str, doc_id: &str)
-> Result<CouchDbDocument, DomainError>;
async fn save_document(&self, db_name: &str, doc: CouchDbDocument)
-> Result<CouchDbDocument, DomainError>;
async fn delete_document(&self, db_name: &str, doc_id: &str, rev: &str)
-> Result<(), DomainError>;
// ビュークエリ
async fn query_view(&self, db_name: &str, design_doc: &str,
view_name: &str, options: Value) -> Result<Vec<CouchDbDocument>, DomainError>;
// データベース管理
async fn ensure_database(&self, db_name: &str) -> Result<(), DomainError>;
// レプリケーション
async fn replicate(&self, source: &str, target: &str, options: Value)
-> Result<Value, DomainError>;
// HTTP プロキシ
async fn forward_request(&self, method: &str, path: &str,
query: Option<String>, headers: HeaderMap, body: Bytes)
-> Result<Response<Body>, DomainError>;
// アクセサ
fn get_base_url(&self) -> String;
fn get_auth_credentials(&self) -> Option<(String, String)>;
}
forward_request の処理フロー:
Obsidian Plugin → /db/* → livesync-proxy → forward_request → CouchDB
リクエストタイプ別の処理:
| エンドポイント | 特徴 | タイムアウト |
|---|---|---|
/_changes?feed=longpoll | 長時間接続 | 120秒 |
/_changes | 通常の変更取得 | 90秒 |
/_bulk_docs | 大量データ | 60秒 |
| その他 | 通常リクエスト | 60秒 |
longpoll リクエストの検出と処理:
let is_longpoll = path.contains("/_changes")
&& query.as_ref().is_some_and(|q| q.contains("feed=longpoll"));
let client = if is_longpoll {
Client::builder()
.timeout(Duration::from_secs(120))
.tcp_keepalive(Some(Duration::from_secs(30)))
.tcp_nodelay(true)
.build()?
} else {
self.client.clone()
};
エラータイプ別の処理:
match e {
// longpoll の中断(正常)
err if is_longpoll && err.to_string().contains("aborted") => {
// 204 No Content を返す
}
// タイムアウト
err if err.is_timeout() => {
// 504 Gateway Timeout を返す
}
// 接続エラー
err if err.is_connect() => {
// 502 Bad Gateway を返す
}
}
MetricsState の使用(interfaces/web/metrics.rs):
// リクエスト記録
state.metrics_state.record_request(&path, method, status_code).await;
// 処理時間記録
state.metrics_state.record_request_duration(&path, method, start);
収集されるメトリクス:
http_requests_total - 総リクエスト数http_request_duration_seconds - レスポンス時間(ヒストグラム)HealthState の実装(interfaces/web/health.rs):
pub struct HealthState {
pub couchdb_status: RwLock<CouchDbStatus>,
consecutive_failures: AtomicU32, // バックオフ用
// ...
}
バックオフ戦略:
let backoff_secs = std::cmp::min(
2u64.pow(failures),
self.max_check_interval.as_secs(),
);
// 1. トレイトにメソッド追加(domain/services.rs)
#[async_trait]
pub trait CouchDbRepository {
// 既存メソッド...
async fn compact_database(&self, db_name: &str) -> Result<(), DomainError>;
}
// 2. CouchDbClient に実装追加(infrastructure/couchdb.rs)
async fn compact_database(&self, db_name: &str) -> Result<(), DomainError> {
let url = format!("{}{}/_compact", self.base_url, db_name);
let response = self.client
.post(&url)
.basic_auth(&self.username, Some(&self.password))
.header("Content-Type", "application/json")
.send()
.await
.map_err(|e| DomainError::CouchDbError(e.to_string()))?;
if !response.status().is_success() {
return Err(DomainError::CouchDbError("Compact failed".into()));
}
Ok(())
}
use mockall::mock;
mock! {
pub CouchDbMock {}
#[async_trait]
impl CouchDbRepository for CouchDbMock {
async fn get_document(&self, db_name: &str, doc_id: &str)
-> Result<CouchDbDocument, DomainError>;
// 他のメソッド...
}
}
#[tokio::test]
async fn test_with_mock() {
let mut mock = MockCouchDbMock::new();
mock.expect_get_document()
.returning(|_, _| Ok(CouchDbDocument { ... }));
}
struct InMemoryCouchDb {
databases: Mutex<HashMap<String, HashMap<String, CouchDbDocument>>>,
}
#[async_trait]
impl CouchDbRepository for InMemoryCouchDb {
async fn get_document(&self, db_name: &str, doc_id: &str)
-> Result<CouchDbDocument, DomainError> {
let dbs = self.databases.lock().unwrap();
dbs.get(db_name)
.and_then(|db| db.get(doc_id))
.cloned()
.ok_or(DomainError::CouchDbError("Not found".into()))
}
}
影響: 16バイト ID → 4バイト ID で、1000万ドキュメントが 21GB → 4GB に削減された事例あり。
推奨:
/ は避け、コロン : を使用(プロキシ互換性)// 推奨: 順序的 ID(タイムスタンプベース)
let doc_id = format!("{}:{}", timestamp_ms, uuid_short);
// 階層的 ID(親子関係)
let child_id = format!("{}:child:{}", parent_id, child_uuid);
多数の小さなドキュメント > 少数の大きなドキュメント
// 推奨: 頻繁に変更されるデータを分離
// メインドキュメント
{ "_id": "note:abc", "title": "...", "created_at": "..." }
// メタデータドキュメント(頻繁に更新)
{ "_id": "note:abc:meta", "view_count": 100, "updated_at": "..." }
メタデータフィールド(推奨):
{
"_id": "...",
"type": "note",
"created_at": "2025-01-15T10:30:00.000Z",
"updated_at": "2025-01-15T10:30:00.000Z",
"created_by": "user_id",
"version": 1
}
日付は ISO 8601 形式(.toISOString())を使用。
ネイティブ関数で高速化(JavaScript → ネイティブで 60秒 → 4秒):
// 遅い: JavaScript reduce
{ "reduce": "function(keys, values) { return sum(values); }" }
// 速い: ネイティブ reduce
{ "reduce": "_sum" }
{ "reduce": "_count" }
{ "reduce": "_stats" }
CouchDB は競合を「first-class citizen」として扱う。
// 競合検出
async fn check_conflicts(&self, db: &str, doc_id: &str) -> Result<Vec<String>, DomainError> {
let url = format!("{}{}/{}?conflicts=true", self.base_url, db, doc_id);
let response = self.client.get(&url)
.basic_auth(&self.username, Some(&self.password))
.send().await?;
// _conflicts フィールドをパース
}
解決戦略:
| 設定 | 推奨値 | 説明 |
|---|---|---|
batch_size | 100-500 | 小さいほどチェックポイント頻度↑、RAM使用量↓ |
checkpoint_interval | 5000ms | 頻繁な更新には低い値 |
worker_processes | 4 | ネットワークスループット向上 |
継続的レプリケーション(リアルタイム同期):
{
"source": "local_db",
"target": "remote_db",
"continuous": true
}
定期的なコンパクションでパフォーマンス維持:
# データベースコンパクション
curl -X POST http://localhost:5984/dbname/_compact
# ビューコンパクション
curl -X POST http://localhost:5984/dbname/_compact/design_doc
livesync-proxy/src/domain/services.rs - CouchDbRepository トレイトlivesync-proxy/src/infrastructure/couchdb.rs - CouchDbClient 実装(674行)livesync-proxy/src/interfaces/web/health.rs - ヘルスチェックlivesync-proxy/src/interfaces/web/metrics.rs - メトリクス/_up - ヘルスチェック/{db} - データベース操作/{db}/{docid} - ドキュメント操作/{db}/_changes - 変更フィード/{db}/_bulk_docs - バルク操作/_replicate - レプリケーション/{db}/_compact - コンパクションProject scaffolding, deployment configuration, and CI/CD setup for Google ADK agents.
Set up tracing, logging, and monitoring for deployed ADK agents across Cloud Trace, BigQuery, and third-party platforms.
Enterprise Azure infrastructure architect generating Bicep or Terraform from workload descriptions.
Plan and configure production-ready Azure Kubernetes Service clusters with Day-0 and Day-1 best practices.
Raw mechanical interfaces fusing Swiss typographic print with military terminal aesthetics. Rigid grids, extreme type scale contrast, utilitarian color, analog degradation effects. For data-heavy dashboards, portfolios, or editorial sites that need to feel like declassified blueprints.
Web search, scraping, extraction, crawling, and monitoring via ScrapeGraph AI CLI.
Skill for working with Firebase Hosting (Classic). Use this when you want to deploy static web apps, Single Page Apps (SPAs), or simple microservices. Do NOT use for Firebase App Hosting.
Deploy and manage web apps with Firebase App Hosting. Use this skill when deploying Next.js/Angular apps with backends.
Deploy applications and websites to Vercel. Use when the user requests deployment actions like "deploy my app", "deploy and give me the link", "push this live", or "create a preview deployment".
Build SEO-optimized pages at scale using templates, data, and proven playbook patterns.
Design and build isolated, reusable Convex backend components with clear boundaries and app-facing wrappers.
Deploy and manage projects on Vercel using token-based authentication. Use when working with Vercel CLI using access tokens rather than interactive login — e.g. "deploy to vercel", "set up vercel", "add environment variables to vercel".
Opus.ai: Revolutionize Your Web Experience
Build a no-code AI app in minutes.
An IDE for code migration from legacy to modern frameworks through coding agents.
Automate CGI animation in live-action scenes
Branded artistic QR-code concepts
Launch a website in seconds with AI.
Streamline Your Coding Experience with AI Code Helper
Convert any screenshot or design to clean code.