UNPKG

@gftdcojp/ksqldb-orm

Version:

ksqldb-orm - Server-Side TypeScript ORM for ksqlDB with enterprise security extensions

372 lines 13.3 kB
"use strict"; /** * Resilient ksqlDB Client - パーティション再バランシング対応の高可用性クライアント * * @description このモジュールは、既存のksqlDBクライアントに * リトライ機構、サーキットブレーカー、フォールバック戦略を統合した * リジリエントなクライアントを提供します。 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ResilientKsqlDbClient = void 0; exports.initializeResilientKsqlDbClient = initializeResilientKsqlDbClient; exports.executePullQueryResilient = executePullQueryResilient; exports.executeQueryResilient = executeQueryResilient; exports.executePushQueryResilient = executePushQueryResilient; exports.getKsqlDbHealth = getKsqlDbHealth; exports.getKsqlDbMetrics = getKsqlDbMetrics; exports.getKsqlDbCircuitBreakerState = getKsqlDbCircuitBreakerState; exports.disposeResilientKsqlDbClient = disposeResilientKsqlDbClient; const types_1 = require("./types"); const ksqldb_client_1 = require("./ksqldb-client"); const resilience_1 = require("./resilience"); const logger_1 = require("./utils/logger"); /** * リジリエント ksqlDB クライアント * * 既存のksqlDBクライアントAPIと完全に互換性を保ちながら、 * パーティション再バランシングなどの障害に対する耐性を提供します。 */ class ResilientKsqlDbClient { constructor(config) { this.initialized = false; this.config = config; // ResilientExecutor を初期化 this.executor = new resilience_1.ResilientExecutor(config.resilience?.retries, config.resilience?.circuitBreaker, config.resilience?.partitionAwareness, config.resilience?.fallback, config.resilience?.metrics); } /** * クライアントを初期化 * * 既存のinitializeKsqlDbClientと同じインターフェース */ async initialize() { if (this.initialized) { return; } const result = await this.executor.execute(() => { (0, ksqldb_client_1.initializeKsqlDbClient)(this.config); return Promise.resolve(); }, 'initialize-ksqldb-client'); if (result.error) { throw new Error(`Failed to initialize ksqlDB client: ${result.error.message}`); } this.initialized = true; logger_1.log.info('Resilient ksqlDB client initialized successfully'); } /** * リジリエント Pull Query 実行 * * 既存のexecutePullQueryと互換性を保ちながら、 * リトライ・サーキットブレーカー・フォールバック機能を提供 * * @param sql SQL文 * @param options 実行オプション(拡張版) * @returns リジリエント実行結果 */ async executePullQuery(sql, options = {}) { this.ensureInitialized(); const { retries, backoffStrategy, fallbackToHttp, timeout, ...pullQueryOptions } = options; // プライマリ操作(通常のクエリ実行) const primaryOperation = () => (0, ksqldb_client_1.executePullQuery)(sql, pullQueryOptions); // フォールバック操作(HTTP経由での実行など) const fallbackOperation = () => { logger_1.log.info('Executing pull query via fallback mechanism', { sql }); // ここでHTTPフォールバックや代替エンドポイントを試行 return (0, ksqldb_client_1.executePullQuery)(sql, { ...pullQueryOptions, format: 'object' }); }; // フォールバック設定がある場合は executeWithFallback を使用 const shouldUseFallback = fallbackToHttp ?? this.config.resilience?.fallback?.fallbackToHttp ?? true; let result; if (shouldUseFallback) { result = await this.executor.executeWithFallback(primaryOperation, fallbackOperation, 'pull-query', { retries, backoffStrategy, timeout }); } else { result = await this.executor.execute(primaryOperation, 'pull-query', { retries, backoffStrategy, timeout }); } return { data: result.data, error: result.error, resilience: result.resilience }; } /** * 下位互換性のためのPull Query実行 * * 既存のAPIと完全に互換性を保持(リジリエンス機能は内部的に適用) * * @param sql SQL文 * @param options 従来のPull Queryオプション * @returns 従来の形式の結果 */ async executePullQueryCompat(sql, options = {}) { const result = await this.executePullQuery(sql, options); // エラーがある場合は従来通り例外をスロー if (result.error) { throw result.error; } return result.data; } /** * リジリエント DDL/DML クエリ実行 * * @param sql SQL文 * @param options 実行オプション * @returns リジリエント実行結果 */ async executeQuery(sql, options = {}) { this.ensureInitialized(); const operation = () => (0, ksqldb_client_1.executeQuery)(sql); const result = await this.executor.execute(operation, 'ddl-dml-query', options); return { data: result.data, error: result.error, resilience: result.resilience }; } /** * 下位互換性のためのクエリ実行 * * @param sql SQL文 * @returns 従来の形式の結果 */ async executeQueryCompat(sql) { const result = await this.executeQuery(sql); if (result.error) { throw result.error; } return result.data; } /** * Push Query実行(ストリーミング) * * Push Queryはストリーミング性質上、リトライよりも * 接続の復旧とエラーハンドリングに重点を置く * * @param sql SQL文 * @param onData データ受信コールバック * @param onError エラーコールバック(拡張版) * @param onComplete 完了コールバック * @returns terminate関数 */ async executePushQuery(sql, onData, onError, onComplete) { this.ensureInitialized(); // Push Queryのエラーハンドリング拡張 const enhancedOnError = (error) => { const errorType = resilience_1.ErrorClassifier.classifyError(error); const canRetry = resilience_1.ErrorClassifier.isRetryableError(errorType); logger_1.log.warn('Push query error detected', { error: error.message, errorType, canRetry }); if (onError) { onError(error, { errorType, retryCount: 0, // Push queryではリトライカウントは管理しない canRetry }); } }; return (0, ksqldb_client_1.executePushQuery)(sql, onData, enhancedOnError, onComplete); } /** * ヘルスチェック実行 * * クライアントの状態とサーキットブレーカーの状態を確認 */ async healthCheck() { const circuitBreakerState = this.executor.getCircuitBreakerState(); const metrics = this.executor.getMetrics(); // 簡単な接続テスト let connectionTest = null; try { connectionTest = await this.executeQueryCompat('SHOW STREAMS;'); } catch (error) { connectionTest = { error: error.message }; } // ステータス判定 let status; if (!this.initialized || circuitBreakerState === types_1.CircuitBreakerState.OPEN) { status = 'unhealthy'; } else if (circuitBreakerState === types_1.CircuitBreakerState.HALF_OPEN || connectionTest?.error) { status = 'degraded'; } else { status = 'healthy'; } return { status, details: { clientInitialized: this.initialized, circuitBreakerState, metrics, lastError: connectionTest?.error } }; } /** * メトリクス情報を取得 */ getMetrics() { return this.executor.getMetrics(); } /** * サーキットブレーカーの状態を取得 */ getCircuitBreakerState() { return this.executor.getCircuitBreakerState(); } /** * サーキットブレーカーをリセット */ resetCircuitBreaker() { this.executor.resetCircuitBreaker(); logger_1.log.info('Circuit breaker has been reset'); } /** * メトリクスをリセット */ resetMetrics() { this.executor.resetMetrics(); logger_1.log.info('Resilience metrics have been reset'); } /** * クライアント設定を取得 */ getConfig() { return { ...this.config }; } /** * リソースクリーンアップ */ dispose() { this.executor.dispose(); this.initialized = false; logger_1.log.info('Resilient ksqlDB client disposed'); } /** * 初期化チェック */ ensureInitialized() { if (!this.initialized) { throw new Error('Resilient ksqlDB client is not initialized. Call initialize() first.'); } } } exports.ResilientKsqlDbClient = ResilientKsqlDbClient; /** * グローバルなリジリエントクライアントインスタンス */ let globalResilientClient = null; /** * リジリエントksqlDBクライアントを初期化(グローバル) * * 既存のinitializeKsqlDbClientと同じインターフェースで、 * リジリエンス機能が追加されたクライアントを初期化 * * @param config リジリエント設定を含むksqlDB設定 */ async function initializeResilientKsqlDbClient(config) { // 既存のクライアントがある場合はクリーンアップ if (globalResilientClient) { globalResilientClient.dispose(); } globalResilientClient = new ResilientKsqlDbClient(config); await globalResilientClient.initialize(); logger_1.log.info('Global resilient ksqlDB client initialized', { url: config.url, resilienceEnabled: !!config.resilience }); } /** * グローバルなリジリエントPull Query実行 * * 既存のexecutePullQueryと同じインターフェース(拡張オプション対応) * * @param sql SQL文 * @param options 拡張されたPull Queryオプション * @returns 実行結果 */ async function executePullQueryResilient(sql, options = {}) { if (!globalResilientClient) { throw new Error('Resilient ksqlDB client is not initialized. Call initializeResilientKsqlDbClient() first.'); } return globalResilientClient.executePullQueryCompat(sql, options); } /** * グローバルなリジリエントQuery実行 * * @param sql SQL文 * @returns 実行結果 */ async function executeQueryResilient(sql) { if (!globalResilientClient) { throw new Error('Resilient ksqlDB client is not initialized. Call initializeResilientKsqlDbClient() first.'); } return globalResilientClient.executeQueryCompat(sql); } /** * グローバルなリジリエントPush Query実行 * * @param sql SQL文 * @param onData データ受信コールバック * @param onError エラーコールバック * @param onComplete 完了コールバック * @returns terminate関数 */ async function executePushQueryResilient(sql, onData, onError, onComplete) { if (!globalResilientClient) { throw new Error('Resilient ksqlDB client is not initialized. Call initializeResilientKsqlDbClient() first.'); } return globalResilientClient.executePushQuery(sql, onData, onError, onComplete); } /** * グローバルクライアントのヘルスチェック */ async function getKsqlDbHealth() { if (!globalResilientClient) { return { status: 'unhealthy', details: { error: 'Client not initialized' } }; } return globalResilientClient.healthCheck(); } /** * グローバルクライアントのメトリクス取得 */ function getKsqlDbMetrics() { if (!globalResilientClient) { return null; } return globalResilientClient.getMetrics(); } /** * グローバルクライアントのサーキットブレーカー状態取得 */ function getKsqlDbCircuitBreakerState() { if (!globalResilientClient) { return null; } return globalResilientClient.getCircuitBreakerState(); } /** * グローバルクライアントのリソースクリーンアップ */ function disposeResilientKsqlDbClient() { if (globalResilientClient) { globalResilientClient.dispose(); globalResilientClient = null; logger_1.log.info('Global resilient ksqlDB client disposed'); } } //# sourceMappingURL=resilient-client.js.map