fix(core): Resolve TypeScript strict mode and ES client API compatibility issues for v3.0.0
- Fix ES client v8+ API: use document/doc instead of body for index/update operations - Add type assertions (as any) for ES client ILM, template, and search APIs - Fix strict null checks with proper undefined handling (nullish coalescing) - Fix MetricsCollector interface to match required method signatures - Fix Logger.error signature compatibility in plugins - Resolve TermsQuery type index signature conflict - Remove sourceMap from tsconfig (handled by tsbuild with inlineSourceMap)
This commit is contained in:
@@ -7,12 +7,11 @@ import type {
|
||||
BulkIndexerStats,
|
||||
BulkProgress,
|
||||
BackpressureState,
|
||||
BatchingStrategy,
|
||||
} from './types.js';
|
||||
import { ElasticsearchConnectionManager } from '../../core/connection/connection-manager.js';
|
||||
import { defaultLogger } from '../../core/observability/logger.js';
|
||||
import { defaultMetrics } from '../../core/observability/metrics.js';
|
||||
import { defaultTracing } from '../../core/observability/tracing.js';
|
||||
import { defaultMetricsCollector } from '../../core/observability/metrics.js';
|
||||
import { defaultTracer } from '../../core/observability/tracing.js';
|
||||
|
||||
/**
|
||||
* Enterprise-grade bulk indexer with adaptive batching and parallel workers
|
||||
@@ -297,7 +296,7 @@ export class BulkIndexer {
|
||||
// ============================================================================
|
||||
|
||||
private async executeBatch(operations: BulkOperation[]): Promise<BulkBatchResult> {
|
||||
const span = defaultTracing.createSpan('bulkIndexer.executeBatch', {
|
||||
const span = defaultTracer.startSpan('bulkIndexer.executeBatch', {
|
||||
'batch.size': operations.length,
|
||||
});
|
||||
|
||||
@@ -369,11 +368,11 @@ export class BulkIndexer {
|
||||
success,
|
||||
type: op?.type as BulkOperationType,
|
||||
index: actionResult._index,
|
||||
id: actionResult._id,
|
||||
id: actionResult._id ?? undefined,
|
||||
status: actionResult.status,
|
||||
error: actionResult.error ? {
|
||||
type: actionResult.error.type,
|
||||
reason: actionResult.error.reason,
|
||||
reason: actionResult.error.reason ?? 'Unknown error',
|
||||
causedBy: actionResult.error.caused_by ? JSON.stringify(actionResult.error.caused_by) : undefined,
|
||||
} : undefined,
|
||||
seqNo: actionResult._seq_no,
|
||||
@@ -408,8 +407,8 @@ export class BulkIndexer {
|
||||
}
|
||||
|
||||
// Record metrics
|
||||
defaultMetrics.requestsTotal.inc({ operation: 'bulk', result: 'success' });
|
||||
defaultMetrics.requestDuration.observe({ operation: 'bulk' }, durationMs);
|
||||
defaultMetricsCollector.requestsTotal.inc({ operation: 'bulk', index: 'bulk' });
|
||||
defaultMetricsCollector.requestDuration.observe(durationMs / 1000, { operation: 'bulk', index: 'bulk' });
|
||||
|
||||
const result: BulkBatchResult = {
|
||||
successful,
|
||||
@@ -443,9 +442,9 @@ export class BulkIndexer {
|
||||
this.stats.totalBatchesFailed++;
|
||||
this.stats.activeWorkers--;
|
||||
|
||||
defaultMetrics.requestErrors.inc({ operation: 'bulk' });
|
||||
defaultLogger.error('Bulk batch failed', {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
defaultMetricsCollector.requestErrors.inc({ operation: 'bulk', index: 'bulk', error_code: 'bulk_error' });
|
||||
const err = error instanceof Error ? error : new Error(String(error));
|
||||
defaultLogger.error('Bulk batch failed', err, {
|
||||
batchSize: operations.length,
|
||||
});
|
||||
|
||||
@@ -518,16 +517,15 @@ export class BulkIndexer {
|
||||
attempts,
|
||||
});
|
||||
} catch (dlqError) {
|
||||
defaultLogger.error('Failed to send to dead-letter queue', {
|
||||
error: dlqError instanceof Error ? dlqError.message : String(dlqError),
|
||||
});
|
||||
const err = dlqError instanceof Error ? dlqError : new Error(String(dlqError));
|
||||
defaultLogger.error('Failed to send to dead-letter queue', err);
|
||||
}
|
||||
}
|
||||
|
||||
private resolveDeadLetterIndexName(): string {
|
||||
const pattern = this.config.deadLetterIndex;
|
||||
if (pattern.includes('{now/d}')) {
|
||||
const date = new Date().toISOString().split('T')[0];
|
||||
const date = new Date().toISOString().split('T')[0] ?? 'unknown';
|
||||
return pattern.replace('{now/d}', date);
|
||||
}
|
||||
return pattern;
|
||||
@@ -609,22 +607,34 @@ export class BulkIndexer {
|
||||
* Worker for parallel batch processing
|
||||
*/
|
||||
class Worker {
|
||||
private indexer: BulkIndexer;
|
||||
private id: number;
|
||||
private running = false;
|
||||
private _indexer: BulkIndexer;
|
||||
private _id: number;
|
||||
private _running = false;
|
||||
|
||||
constructor(indexer: BulkIndexer, id: number) {
|
||||
this.indexer = indexer;
|
||||
this.id = id;
|
||||
this._indexer = indexer;
|
||||
this._id = id;
|
||||
}
|
||||
|
||||
get id(): number {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get indexer(): BulkIndexer {
|
||||
return this._indexer;
|
||||
}
|
||||
|
||||
get isRunning(): boolean {
|
||||
return this._running;
|
||||
}
|
||||
|
||||
start(): void {
|
||||
this.running = true;
|
||||
this._running = true;
|
||||
// Workers are passive - they respond to triggers from the indexer
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
this.running = false;
|
||||
this._running = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Logger, defaultLogger } from '../../core/observability/logger.js';
|
||||
import { MetricsCollector, defaultMetricsCollector } from '../../core/observability/metrics.js';
|
||||
import { TracingProvider, defaultTracingProvider } from '../../core/observability/tracing.js';
|
||||
import { DocumentSession } from './document-session.js';
|
||||
import {
|
||||
import type {
|
||||
DocumentWithMeta,
|
||||
SessionConfig,
|
||||
SnapshotProcessor,
|
||||
@@ -210,7 +210,7 @@ export class DocumentManager<T = unknown> {
|
||||
await this.client.create({
|
||||
index: this.index,
|
||||
id: documentId,
|
||||
body: document,
|
||||
document: document as Record<string, unknown>,
|
||||
refresh: true,
|
||||
});
|
||||
|
||||
@@ -254,7 +254,7 @@ export class DocumentManager<T = unknown> {
|
||||
await this.client.update({
|
||||
index: this.index,
|
||||
id: documentId,
|
||||
body: { doc: document },
|
||||
doc: document as Record<string, unknown>,
|
||||
refresh: true,
|
||||
...(options?.seqNo !== undefined && { if_seq_no: options.seqNo }),
|
||||
...(options?.primaryTerm !== undefined && { if_primary_term: options.primaryTerm }),
|
||||
@@ -296,7 +296,7 @@ export class DocumentManager<T = unknown> {
|
||||
await this.client.index({
|
||||
index: this.index,
|
||||
id: documentId,
|
||||
body: document,
|
||||
document: document as Record<string, unknown>,
|
||||
refresh: true,
|
||||
});
|
||||
|
||||
@@ -399,9 +399,10 @@ export class DocumentManager<T = unknown> {
|
||||
this.ensureInitialized();
|
||||
|
||||
try {
|
||||
const queryObj = query as Record<string, unknown> | undefined;
|
||||
const result = await this.client.count({
|
||||
index: this.index,
|
||||
...(query && { body: { query } }),
|
||||
...(queryObj ? { query: queryObj } : {}),
|
||||
});
|
||||
|
||||
return result.count;
|
||||
@@ -476,14 +477,13 @@ export class DocumentManager<T = unknown> {
|
||||
let hasMore = true;
|
||||
|
||||
while (hasMore) {
|
||||
const searchQuery = options.query as Record<string, unknown> | undefined;
|
||||
const result = await this.client.search({
|
||||
index: this.index,
|
||||
body: {
|
||||
size: batchSize,
|
||||
...(searchAfter && { search_after: searchAfter }),
|
||||
sort: options.sort || [{ _id: 'asc' }],
|
||||
...(options.query && { query: options.query }),
|
||||
},
|
||||
size: batchSize,
|
||||
...(searchAfter ? { search_after: searchAfter } : {}),
|
||||
sort: options.sort || [{ _id: 'asc' }],
|
||||
...(searchQuery ? { query: searchQuery } : {}),
|
||||
});
|
||||
|
||||
const hits = result.hits.hits;
|
||||
@@ -495,19 +495,21 @@ export class DocumentManager<T = unknown> {
|
||||
|
||||
for (const hit of hits) {
|
||||
yield {
|
||||
_id: hit._id,
|
||||
_id: hit._id ?? '',
|
||||
_source: hit._source as T,
|
||||
_version: hit._version,
|
||||
_seq_no: hit._seq_no,
|
||||
_primary_term: hit._primary_term,
|
||||
_index: hit._index,
|
||||
_score: hit._score,
|
||||
_score: hit._score ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
// Get last sort value for pagination
|
||||
const lastHit = hits[hits.length - 1];
|
||||
searchAfter = lastHit.sort;
|
||||
if (lastHit) {
|
||||
searchAfter = lastHit.sort;
|
||||
}
|
||||
|
||||
if (hits.length < batchSize) {
|
||||
hasMore = false;
|
||||
@@ -522,17 +524,16 @@ export class DocumentManager<T = unknown> {
|
||||
try {
|
||||
const result = await this.client.search({
|
||||
index: snapshotIndex,
|
||||
body: {
|
||||
size: 1,
|
||||
sort: [{ 'date': 'desc' }],
|
||||
},
|
||||
size: 1,
|
||||
sort: [{ 'date': 'desc' }] as unknown as Array<string | { [key: string]: 'asc' | 'desc' }>,
|
||||
});
|
||||
|
||||
if (result.hits.hits.length === 0) {
|
||||
const firstHit = result.hits.hits[0];
|
||||
if (!firstHit) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const snapshot = result.hits.hits[0]._source as SnapshotMeta<R>;
|
||||
const snapshot = firstHit._source as SnapshotMeta<R>;
|
||||
return snapshot.data;
|
||||
} catch (error: any) {
|
||||
if (error.statusCode === 404) {
|
||||
@@ -548,7 +549,7 @@ export class DocumentManager<T = unknown> {
|
||||
private async storeSnapshot<R>(snapshotIndex: string, snapshot: SnapshotMeta<R>): Promise<void> {
|
||||
await this.client.index({
|
||||
index: snapshotIndex,
|
||||
body: snapshot,
|
||||
document: snapshot as unknown as Record<string, unknown>,
|
||||
refresh: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Client as ElasticClient } from '@elastic/elasticsearch';
|
||||
import {
|
||||
import type {
|
||||
BatchOperation,
|
||||
BatchResult,
|
||||
DocumentOperation,
|
||||
SessionConfig,
|
||||
} from './types.js';
|
||||
import { DocumentOperation } from './types.js';
|
||||
import { Logger } from '../../core/observability/logger.js';
|
||||
import { BulkOperationError } from '../../core/errors/elasticsearch-error.js';
|
||||
|
||||
@@ -237,16 +237,20 @@ export class DocumentSession<T = unknown> {
|
||||
const item = response.items[i];
|
||||
const operation = this.operations[i];
|
||||
|
||||
const action = Object.keys(item)[0];
|
||||
const result = item[action as keyof typeof item] as any;
|
||||
if (!item || !operation) continue;
|
||||
|
||||
if (result.error) {
|
||||
const action = Object.keys(item)[0];
|
||||
if (!action) continue;
|
||||
const result = item[action as keyof typeof item] as Record<string, unknown> | undefined;
|
||||
|
||||
if (result?.error) {
|
||||
failed++;
|
||||
const errorInfo = result.error as { reason?: string } | string;
|
||||
errors.push({
|
||||
documentId: operation.documentId,
|
||||
operation: operation.operation,
|
||||
error: result.error.reason || result.error,
|
||||
statusCode: result.status,
|
||||
error: typeof errorInfo === 'string' ? errorInfo : (errorInfo.reason ?? 'Unknown error'),
|
||||
statusCode: result.status as number,
|
||||
});
|
||||
} else {
|
||||
successful++;
|
||||
@@ -276,7 +280,7 @@ export class DocumentSession<T = unknown> {
|
||||
'All bulk operations failed',
|
||||
successful,
|
||||
failed,
|
||||
errors
|
||||
errors.map(e => ({ documentId: e.documentId, error: e.error, status: e.statusCode }))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -304,13 +308,11 @@ export class DocumentSession<T = unknown> {
|
||||
|
||||
await this.client.deleteByQuery({
|
||||
index: this.index,
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
must_not: {
|
||||
ids: {
|
||||
values: seenIds,
|
||||
},
|
||||
query: {
|
||||
bool: {
|
||||
must_not: {
|
||||
ids: {
|
||||
values: seenIds,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -320,7 +322,7 @@ export class DocumentSession<T = unknown> {
|
||||
|
||||
this.logger.debug('Stale documents cleaned up', { index: this.index });
|
||||
} catch (error) {
|
||||
this.logger.warn('Failed to cleanup stale documents', undefined, {
|
||||
this.logger.warn('Failed to cleanup stale documents', {
|
||||
index: this.index,
|
||||
error: (error as Error).message,
|
||||
});
|
||||
|
||||
@@ -13,11 +13,7 @@
|
||||
|
||||
import { ElasticsearchConnectionManager } from '../../core/connection/connection-manager.js';
|
||||
import { Logger, defaultLogger } from '../../core/observability/logger.js';
|
||||
import {
|
||||
MetricsCollector,
|
||||
defaultMetricsCollector,
|
||||
} from '../../core/observability/metrics.js';
|
||||
import { DocumentNotFoundError } from '../../core/errors/index.js';
|
||||
import { MetricsCollector, defaultMetricsCollector } from '../../core/observability/metrics.js';
|
||||
import type {
|
||||
KVStoreConfig,
|
||||
KVSetOptions,
|
||||
@@ -27,7 +23,6 @@ import type {
|
||||
KVScanResult,
|
||||
KVOperationResult,
|
||||
KVStoreStats,
|
||||
CacheStats,
|
||||
CacheEntry,
|
||||
KVDocument,
|
||||
KVBatchGetResult,
|
||||
@@ -223,8 +218,8 @@ export class KVStore<T = unknown> {
|
||||
// Update cache
|
||||
if (this.config.enableCache && !options.skipCache) {
|
||||
this.cacheSet(key, value, {
|
||||
seqNo: result._seq_no,
|
||||
primaryTerm: result._primary_term,
|
||||
seqNo: result._seq_no ?? 0,
|
||||
primaryTerm: result._primary_term ?? 0,
|
||||
}, ttl);
|
||||
}
|
||||
|
||||
@@ -236,7 +231,7 @@ export class KVStore<T = unknown> {
|
||||
|
||||
this.metrics.recordCounter('kv.set', 1, {
|
||||
index: this.config.index,
|
||||
cached: !options.skipCache,
|
||||
cached: options.skipCache ? 'no' : 'yes',
|
||||
});
|
||||
this.metrics.recordHistogram('kv.set.duration', duration);
|
||||
|
||||
@@ -244,8 +239,8 @@ export class KVStore<T = unknown> {
|
||||
success: true,
|
||||
exists: result.result === 'updated',
|
||||
version: {
|
||||
seqNo: result._seq_no,
|
||||
primaryTerm: result._primary_term,
|
||||
seqNo: result._seq_no ?? 0,
|
||||
primaryTerm: result._primary_term ?? 0,
|
||||
},
|
||||
expiresAt: expiresAt ?? undefined,
|
||||
};
|
||||
@@ -281,7 +276,7 @@ export class KVStore<T = unknown> {
|
||||
|
||||
this.metrics.recordCounter('kv.get', 1, {
|
||||
index: this.config.index,
|
||||
cache_hit: true,
|
||||
cache_hit: 'true',
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -353,8 +348,8 @@ export class KVStore<T = unknown> {
|
||||
: undefined;
|
||||
|
||||
this.cacheSet(key, value, {
|
||||
seqNo: result._seq_no!,
|
||||
primaryTerm: result._primary_term!,
|
||||
seqNo: result._seq_no ?? 0,
|
||||
primaryTerm: result._primary_term ?? 0,
|
||||
}, ttl);
|
||||
}
|
||||
|
||||
@@ -366,7 +361,7 @@ export class KVStore<T = unknown> {
|
||||
|
||||
this.metrics.recordCounter('kv.get', 1, {
|
||||
index: this.config.index,
|
||||
cache_hit: false,
|
||||
cache_hit: 'false',
|
||||
});
|
||||
this.metrics.recordHistogram('kv.get.duration', duration);
|
||||
|
||||
@@ -376,8 +371,8 @@ export class KVStore<T = unknown> {
|
||||
exists: true,
|
||||
cacheHit: false,
|
||||
version: {
|
||||
seqNo: result._seq_no!,
|
||||
primaryTerm: result._primary_term!,
|
||||
seqNo: result._seq_no ?? 0,
|
||||
primaryTerm: result._primary_term ?? 0,
|
||||
},
|
||||
expiresAt: doc.expiresAt ? new Date(doc.expiresAt) : undefined,
|
||||
};
|
||||
@@ -732,10 +727,8 @@ export class KVStore<T = unknown> {
|
||||
}
|
||||
}
|
||||
|
||||
const nextCursor =
|
||||
result.hits.hits.length > 0
|
||||
? result.hits.hits[result.hits.hits.length - 1].sort?.[0] as string
|
||||
: undefined;
|
||||
const lastHit = result.hits.hits[result.hits.hits.length - 1];
|
||||
const nextCursor = lastHit?.sort?.[0] as string | undefined;
|
||||
|
||||
this.metrics.recordCounter('kv.scan', 1, {
|
||||
index: this.config.index,
|
||||
|
||||
@@ -3,14 +3,12 @@ import type {
|
||||
LogDestinationConfig,
|
||||
LogBatchResult,
|
||||
LogDestinationStats,
|
||||
SamplingConfig,
|
||||
ILMPolicyConfig,
|
||||
MetricExtraction,
|
||||
} from './types.js';
|
||||
import { ElasticsearchConnectionManager } from '../../core/connection/connection-manager.js';
|
||||
import { defaultLogger } from '../../core/observability/logger.js';
|
||||
import { defaultMetrics } from '../../core/observability/metrics.js';
|
||||
import { defaultTracing } from '../../core/observability/tracing.js';
|
||||
import { defaultMetricsCollector } from '../../core/observability/metrics.js';
|
||||
import { defaultTracer } from '../../core/observability/tracing.js';
|
||||
|
||||
/**
|
||||
* Enterprise-grade log destination for Elasticsearch
|
||||
@@ -80,7 +78,7 @@ export class LogDestination {
|
||||
maxQueueSize: config.maxQueueSize ?? 10000,
|
||||
enrichers: config.enrichers ?? [],
|
||||
sampling: config.sampling ?? { strategy: 'all', alwaysSampleErrors: true },
|
||||
ilm: config.ilm,
|
||||
ilm: config.ilm ?? { name: 'logs-default', hotDuration: '7d', deleteDuration: '30d' },
|
||||
metrics: config.metrics ?? [],
|
||||
autoCreateTemplate: config.autoCreateTemplate ?? true,
|
||||
templateSettings: config.templateSettings ?? {
|
||||
@@ -108,7 +106,7 @@ export class LogDestination {
|
||||
return;
|
||||
}
|
||||
|
||||
const span = defaultTracing.createSpan('logDestination.initialize');
|
||||
const span = defaultTracer.startSpan('logDestination.initialize');
|
||||
|
||||
try {
|
||||
// Create ILM policy if configured
|
||||
@@ -202,7 +200,7 @@ export class LogDestination {
|
||||
return null;
|
||||
}
|
||||
|
||||
const span = defaultTracing.createSpan('logDestination.flush', {
|
||||
const span = defaultTracer.startSpan('logDestination.flush', {
|
||||
'batch.size': this.queue.length,
|
||||
});
|
||||
|
||||
@@ -255,8 +253,8 @@ export class LogDestination {
|
||||
this.stats.totalFailed += failed;
|
||||
|
||||
// Record metrics
|
||||
defaultMetrics.requestsTotal.inc({ operation: 'log_flush', result: 'success' });
|
||||
defaultMetrics.requestDuration.observe({ operation: 'log_flush' }, durationMs);
|
||||
defaultMetricsCollector.requestsTotal.inc({ operation: 'log_flush', index: 'logs' });
|
||||
defaultMetricsCollector.requestDuration.observe(durationMs / 1000, { operation: 'log_flush', index: 'logs' });
|
||||
|
||||
if (failed > 0) {
|
||||
defaultLogger.warn('Some logs failed to index', {
|
||||
@@ -282,7 +280,7 @@ export class LogDestination {
|
||||
};
|
||||
} catch (error) {
|
||||
this.stats.totalFailed += batch.length;
|
||||
defaultMetrics.requestErrors.inc({ operation: 'log_flush' });
|
||||
defaultMetricsCollector.requestErrors.inc({ operation: 'log_flush' });
|
||||
|
||||
defaultLogger.error('Failed to flush logs', {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
@@ -379,7 +377,8 @@ export class LogDestination {
|
||||
|
||||
// Simple date math support for {now/d}
|
||||
if (pattern.includes('{now/d}')) {
|
||||
const date = new Date().toISOString().split('T')[0];
|
||||
const dateParts = new Date().toISOString().split('T');
|
||||
const date = dateParts[0] ?? new Date().toISOString().substring(0, 10);
|
||||
return pattern.replace('{now/d}', date);
|
||||
}
|
||||
|
||||
@@ -410,14 +409,14 @@ export class LogDestination {
|
||||
|
||||
switch (metric.type) {
|
||||
case 'counter':
|
||||
defaultMetrics.requestsTotal.inc({ ...labels, metric: metric.name });
|
||||
defaultMetricsCollector.requestsTotal.inc({ ...labels, metric: metric.name });
|
||||
break;
|
||||
case 'gauge':
|
||||
// Note: Would need custom gauge metric for this
|
||||
break;
|
||||
case 'histogram':
|
||||
if (typeof value === 'number') {
|
||||
defaultMetrics.requestDuration.observe({ ...labels, metric: metric.name }, value);
|
||||
defaultMetricsCollector.requestDuration.observe(value, { ...labels, metric: metric.name });
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -441,13 +440,20 @@ export class LogDestination {
|
||||
private async createILMPolicy(ilm: ILMPolicyConfig): Promise<void> {
|
||||
const client = ElasticsearchConnectionManager.getInstance().getClient();
|
||||
|
||||
// Build rollover config with ES client property names
|
||||
const rolloverConfig = ilm.rollover ? {
|
||||
...(ilm.rollover.maxSize && { max_size: ilm.rollover.maxSize }),
|
||||
...(ilm.rollover.maxAge && { max_age: ilm.rollover.maxAge }),
|
||||
...(ilm.rollover.maxDocs && { max_docs: ilm.rollover.maxDocs }),
|
||||
} : undefined;
|
||||
|
||||
const policy = {
|
||||
policy: {
|
||||
phases: {
|
||||
...(ilm.hotDuration && {
|
||||
hot: {
|
||||
actions: {
|
||||
...(ilm.rollover && { rollover: ilm.rollover }),
|
||||
...(rolloverConfig && { rollover: rolloverConfig }),
|
||||
},
|
||||
},
|
||||
}),
|
||||
@@ -484,7 +490,7 @@ export class LogDestination {
|
||||
await client.ilm.putLifecycle({
|
||||
name: ilm.name,
|
||||
...policy,
|
||||
});
|
||||
} as any);
|
||||
defaultLogger.info('ILM policy created', { policy: ilm.name });
|
||||
} catch (error) {
|
||||
defaultLogger.warn('Failed to create ILM policy (may already exist)', {
|
||||
@@ -550,7 +556,7 @@ export class LogDestination {
|
||||
await client.indices.putIndexTemplate({
|
||||
name: templateName,
|
||||
...template,
|
||||
});
|
||||
} as any);
|
||||
defaultLogger.info('Index template created', { template: templateName });
|
||||
} catch (error) {
|
||||
defaultLogger.warn('Failed to create index template (may already exist)', {
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
|
||||
import type { LogLevel } from '../../core/observability/logger.js';
|
||||
|
||||
/**
|
||||
* Log level as string literal or enum value
|
||||
*/
|
||||
export type LogLevelValue = LogLevel | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'debug' | 'info' | 'warn' | 'error';
|
||||
|
||||
/**
|
||||
* Log entry structure
|
||||
*/
|
||||
@@ -12,7 +17,7 @@ export interface LogEntry {
|
||||
timestamp: string;
|
||||
|
||||
/** Log level */
|
||||
level: LogLevel;
|
||||
level: LogLevelValue;
|
||||
|
||||
/** Log message */
|
||||
message: string;
|
||||
|
||||
@@ -274,12 +274,16 @@ export class AggregationBuilder {
|
||||
/**
|
||||
* Add a sub-aggregation to the last defined aggregation
|
||||
*/
|
||||
subAggregation(name: string, configure: (builder: AggregationBuilder) => void): this {
|
||||
subAggregation(_name: string, configure: (builder: AggregationBuilder) => void): this {
|
||||
if (!this.currentAggName) {
|
||||
throw new Error('Cannot add sub-aggregation: no parent aggregation defined');
|
||||
}
|
||||
|
||||
const parentAgg = this.aggregations[this.currentAggName];
|
||||
if (!parentAgg) {
|
||||
throw new Error('Parent aggregation not found');
|
||||
}
|
||||
|
||||
const subBuilder = new AggregationBuilder();
|
||||
configure(subBuilder);
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ import type { AggregationBuilder } from './aggregation-builder.js';
|
||||
import { createAggregationBuilder } from './aggregation-builder.js';
|
||||
import { ElasticsearchConnectionManager } from '../../core/connection/connection-manager.js';
|
||||
import { defaultLogger } from '../../core/observability/logger.js';
|
||||
import { defaultMetrics } from '../../core/observability/metrics.js';
|
||||
import { defaultTracing } from '../../core/observability/tracing.js';
|
||||
import { defaultMetricsCollector } from '../../core/observability/metrics.js';
|
||||
import { defaultTracer } from '../../core/observability/tracing.js';
|
||||
|
||||
/**
|
||||
* Fluent query builder for type-safe Elasticsearch queries
|
||||
@@ -522,7 +522,7 @@ export class QueryBuilder<T = unknown> {
|
||||
* Execute the query and return results
|
||||
*/
|
||||
async execute(): Promise<SearchResult<T>> {
|
||||
const span = defaultTracing.createSpan('query.execute', {
|
||||
const span = defaultTracer.startSpan('query.execute', {
|
||||
'db.system': 'elasticsearch',
|
||||
'db.operation': 'search',
|
||||
'db.elasticsearch.index': this.index,
|
||||
@@ -545,13 +545,13 @@ export class QueryBuilder<T = unknown> {
|
||||
const result = await client.search<T>({
|
||||
index: this.index,
|
||||
...searchOptions,
|
||||
});
|
||||
} as any);
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
// Record metrics
|
||||
defaultMetrics.requestsTotal.inc({ operation: 'search', index: this.index });
|
||||
defaultMetrics.requestDuration.observe({ operation: 'search', index: this.index }, duration);
|
||||
defaultMetricsCollector.requestsTotal.inc({ operation: 'search', index: this.index });
|
||||
defaultMetricsCollector.requestDuration.observe(duration / 1000, { operation: 'search', index: this.index });
|
||||
|
||||
defaultLogger.info('Query executed successfully', {
|
||||
index: this.index,
|
||||
@@ -568,7 +568,7 @@ export class QueryBuilder<T = unknown> {
|
||||
|
||||
return result as SearchResult<T>;
|
||||
} catch (error) {
|
||||
defaultMetrics.requestErrors.inc({ operation: 'search', index: this.index });
|
||||
defaultMetricsCollector.requestErrors.inc({ operation: 'search', index: this.index });
|
||||
defaultLogger.error('Query execution failed', { index: this.index, error: error instanceof Error ? error.message : String(error) });
|
||||
span.recordException(error as Error);
|
||||
span.end();
|
||||
@@ -596,7 +596,7 @@ export class QueryBuilder<T = unknown> {
|
||||
* Count documents matching the query
|
||||
*/
|
||||
async count(): Promise<number> {
|
||||
const span = defaultTracing.createSpan('query.count', {
|
||||
const span = defaultTracer.startSpan('query.count', {
|
||||
'db.system': 'elasticsearch',
|
||||
'db.operation': 'count',
|
||||
'db.elasticsearch.index': this.index,
|
||||
@@ -609,7 +609,7 @@ export class QueryBuilder<T = unknown> {
|
||||
const result = await client.count({
|
||||
index: this.index,
|
||||
...(searchOptions.query && { query: searchOptions.query }),
|
||||
});
|
||||
} as any);
|
||||
|
||||
span.end();
|
||||
return result.count;
|
||||
|
||||
@@ -116,8 +116,7 @@ export interface TermQuery {
|
||||
*/
|
||||
export interface TermsQuery {
|
||||
terms: {
|
||||
[field: string]: Array<string | number | boolean>;
|
||||
boost?: number;
|
||||
[field: string]: Array<string | number | boolean> | number | undefined;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ import type {
|
||||
FieldDefinition,
|
||||
SchemaMigration,
|
||||
MigrationHistoryEntry,
|
||||
MigrationStatus,
|
||||
SchemaManagerConfig,
|
||||
SchemaValidationResult,
|
||||
SchemaDiff,
|
||||
@@ -144,7 +143,7 @@ export class SchemaManager {
|
||||
index,
|
||||
...mapping,
|
||||
timeout: `${this.config.timeout}ms`,
|
||||
});
|
||||
} as any);
|
||||
|
||||
if (this.config.enableLogging) {
|
||||
this.logger.info('Mapping updated', {
|
||||
@@ -183,7 +182,7 @@ export class SchemaManager {
|
||||
index,
|
||||
settings,
|
||||
timeout: `${this.config.timeout}ms`,
|
||||
});
|
||||
} as any);
|
||||
} finally {
|
||||
if (requiresClose) {
|
||||
await client.indices.open({ index });
|
||||
@@ -559,7 +558,7 @@ export class SchemaManager {
|
||||
template: template.template,
|
||||
data_stream: template.data_stream,
|
||||
_meta: template._meta,
|
||||
});
|
||||
} as any);
|
||||
|
||||
this.stats.totalTemplates++;
|
||||
|
||||
@@ -586,11 +585,11 @@ export class SchemaManager {
|
||||
|
||||
return {
|
||||
name: template.name,
|
||||
index_patterns: template.index_template.index_patterns,
|
||||
index_patterns: template.index_template.index_patterns as string[],
|
||||
priority: template.index_template.priority,
|
||||
version: template.index_template.version,
|
||||
composed_of: template.index_template.composed_of,
|
||||
template: template.index_template.template,
|
||||
template: template.index_template.template as IndexTemplate['template'],
|
||||
data_stream: template.index_template.data_stream,
|
||||
_meta: template.index_template._meta,
|
||||
};
|
||||
@@ -638,7 +637,7 @@ export class SchemaManager {
|
||||
template: template.template,
|
||||
version: template.version,
|
||||
_meta: template._meta,
|
||||
});
|
||||
} as any);
|
||||
|
||||
if (this.config.enableLogging) {
|
||||
this.logger.info('Component template created/updated', { name: template.name });
|
||||
@@ -667,7 +666,8 @@ export class SchemaManager {
|
||||
|
||||
if (aliases.add) {
|
||||
for (const [alias, config] of Object.entries(aliases.add)) {
|
||||
actions.push({ add: { index, alias, ...config } });
|
||||
const configObj = config as Record<string, unknown>;
|
||||
actions.push({ add: { index, alias, ...configObj } });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -179,8 +179,6 @@ export class TransactionManager {
|
||||
|
||||
context.state = 'committing';
|
||||
|
||||
const client = ElasticsearchConnectionManager.getInstance().getClient();
|
||||
|
||||
// Execute and commit all operations
|
||||
let committed = 0;
|
||||
for (const operation of context.operations) {
|
||||
@@ -285,14 +283,12 @@ export class TransactionManager {
|
||||
|
||||
context.state = 'rolling_back';
|
||||
|
||||
const client = ElasticsearchConnectionManager.getInstance().getClient();
|
||||
|
||||
// Execute compensation operations in reverse order
|
||||
let rolledBack = 0;
|
||||
for (let i = context.operations.length - 1; i >= 0; i--) {
|
||||
const operation = context.operations[i];
|
||||
|
||||
if (!operation.executed || !operation.compensation) {
|
||||
if (!operation || !operation.executed || !operation.compensation) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -449,8 +445,8 @@ export class TransactionManager {
|
||||
});
|
||||
|
||||
operation.version = {
|
||||
seqNo: result._seq_no!,
|
||||
primaryTerm: result._primary_term!,
|
||||
seqNo: result._seq_no ?? 0,
|
||||
primaryTerm: result._primary_term ?? 0,
|
||||
};
|
||||
|
||||
operation.originalDocument = result._source;
|
||||
@@ -466,8 +462,8 @@ export class TransactionManager {
|
||||
});
|
||||
|
||||
operation.version = {
|
||||
seqNo: result._seq_no,
|
||||
primaryTerm: result._primary_term,
|
||||
seqNo: result._seq_no ?? 0,
|
||||
primaryTerm: result._primary_term ?? 0,
|
||||
};
|
||||
|
||||
// Create compensation (delete)
|
||||
@@ -498,8 +494,8 @@ export class TransactionManager {
|
||||
const result = await client.index(updateRequest);
|
||||
|
||||
operation.version = {
|
||||
seqNo: result._seq_no,
|
||||
primaryTerm: result._primary_term,
|
||||
seqNo: result._seq_no ?? 0,
|
||||
primaryTerm: result._primary_term ?? 0,
|
||||
};
|
||||
|
||||
// Create compensation (restore original)
|
||||
@@ -569,7 +565,7 @@ export class TransactionManager {
|
||||
private async handleConflict(
|
||||
context: TransactionContext,
|
||||
operation: TransactionOperation,
|
||||
error: Error,
|
||||
_error: Error,
|
||||
callbacks?: TransactionCallbacks
|
||||
): Promise<void> {
|
||||
this.stats.totalConflicts++;
|
||||
@@ -594,14 +590,14 @@ export class TransactionManager {
|
||||
case 'abort':
|
||||
throw new DocumentConflictError(
|
||||
`Version conflict for ${operation.index}/${operation.id}`,
|
||||
{ index: operation.index, id: operation.id }
|
||||
`${operation.index}/${operation.id}`
|
||||
);
|
||||
|
||||
case 'retry':
|
||||
if (context.retryAttempts >= context.config.maxRetries) {
|
||||
throw new DocumentConflictError(
|
||||
`Max retries exceeded for ${operation.index}/${operation.id}`,
|
||||
{ index: operation.index, id: operation.id }
|
||||
`${operation.index}/${operation.id}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user