memento-mcp-server 1.16.1-a → 1.16.2-a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +4 -3
- package/dist/client/index.js.map +1 -1
- package/dist/domains/anchor/services/anchor/anchor-interfaces.d.ts +2 -4
- package/dist/domains/anchor/services/anchor/anchor-interfaces.d.ts.map +1 -1
- package/dist/domains/anchor/services/anchor/anchor-interfaces.js.map +1 -1
- package/dist/domains/anchor/services/anchor/anchor-manager.d.ts +2 -2
- package/dist/domains/anchor/services/anchor/anchor-manager.d.ts.map +1 -1
- package/dist/domains/anchor/services/anchor/anchor-manager.js.map +1 -1
- package/dist/domains/anchor/services/anchor/anchor-search-service.d.ts +11 -34
- package/dist/domains/anchor/services/anchor/anchor-search-service.d.ts.map +1 -1
- package/dist/domains/anchor/services/anchor/anchor-search-service.js +66 -549
- package/dist/domains/anchor/services/anchor/anchor-search-service.js.map +1 -1
- package/dist/domains/anchor/services/anchor/database-types.d.ts +28 -0
- package/dist/domains/anchor/services/anchor/database-types.d.ts.map +1 -0
- package/dist/domains/anchor/services/anchor/database-types.js +6 -0
- package/dist/domains/anchor/services/anchor/database-types.js.map +1 -0
- package/dist/domains/anchor/services/anchor/embedding-types.d.ts +20 -0
- package/dist/domains/anchor/services/anchor/embedding-types.d.ts.map +1 -0
- package/dist/domains/anchor/services/anchor/embedding-types.js +6 -0
- package/dist/domains/anchor/services/anchor/embedding-types.js.map +1 -0
- package/dist/domains/anchor/services/anchor/fallback-search-service.d.ts +36 -0
- package/dist/domains/anchor/services/anchor/fallback-search-service.d.ts.map +1 -0
- package/dist/domains/anchor/services/anchor/fallback-search-service.js +85 -0
- package/dist/domains/anchor/services/anchor/fallback-search-service.js.map +1 -0
- package/dist/domains/anchor/services/anchor/fallback-strategy.d.ts +25 -0
- package/dist/domains/anchor/services/anchor/fallback-strategy.d.ts.map +1 -0
- package/dist/domains/anchor/services/anchor/fallback-strategy.js +35 -0
- package/dist/domains/anchor/services/anchor/fallback-strategy.js.map +1 -0
- package/dist/domains/anchor/services/anchor/local-search-service.d.ts +72 -0
- package/dist/domains/anchor/services/anchor/local-search-service.d.ts.map +1 -0
- package/dist/domains/anchor/services/anchor/local-search-service.js +129 -0
- package/dist/domains/anchor/services/anchor/local-search-service.js.map +1 -0
- package/dist/domains/anchor/services/anchor/n-hop-search-service.d.ts +90 -0
- package/dist/domains/anchor/services/anchor/n-hop-search-service.d.ts.map +1 -0
- package/dist/domains/anchor/services/anchor/n-hop-search-service.js +385 -0
- package/dist/domains/anchor/services/anchor/n-hop-search-service.js.map +1 -0
- package/dist/domains/anchor/services/anchor/n-hop-search-strategy.d.ts +24 -0
- package/dist/domains/anchor/services/anchor/n-hop-search-strategy.d.ts.map +1 -0
- package/dist/domains/anchor/services/anchor/n-hop-search-strategy.js +39 -0
- package/dist/domains/anchor/services/anchor/n-hop-search-strategy.js.map +1 -0
- package/dist/domains/anchor/services/anchor/query-filter-service.d.ts +52 -0
- package/dist/domains/anchor/services/anchor/query-filter-service.d.ts.map +1 -0
- package/dist/domains/anchor/services/anchor/query-filter-service.js +136 -0
- package/dist/domains/anchor/services/anchor/query-filter-service.js.map +1 -0
- package/dist/domains/anchor/services/anchor/query-filter-strategy.d.ts +24 -0
- package/dist/domains/anchor/services/anchor/query-filter-strategy.d.ts.map +1 -0
- package/dist/domains/anchor/services/anchor/query-filter-strategy.js +34 -0
- package/dist/domains/anchor/services/anchor/query-filter-strategy.js.map +1 -0
- package/dist/domains/anchor/services/anchor/search-strategy-interfaces.d.ts +52 -0
- package/dist/domains/anchor/services/anchor/search-strategy-interfaces.d.ts.map +1 -0
- package/dist/domains/anchor/services/anchor/search-strategy-interfaces.js +6 -0
- package/dist/domains/anchor/services/anchor/search-strategy-interfaces.js.map +1 -0
- package/dist/domains/anchor/services/anchor/vector-search-engine-types.d.ts +18 -0
- package/dist/domains/anchor/services/anchor/vector-search-engine-types.d.ts.map +1 -0
- package/dist/domains/anchor/services/anchor/vector-search-engine-types.js +11 -0
- package/dist/domains/anchor/services/anchor/vector-search-engine-types.js.map +1 -0
- package/dist/domains/memory/repositories/core-memory-database.interface.d.ts +47 -0
- package/dist/domains/memory/repositories/core-memory-database.interface.d.ts.map +1 -0
- package/dist/domains/memory/repositories/core-memory-database.interface.js +7 -0
- package/dist/domains/memory/repositories/core-memory-database.interface.js.map +1 -0
- package/dist/domains/memory/repositories/core-memory-repository.d.ts +7 -77
- package/dist/domains/memory/repositories/core-memory-repository.d.ts.map +1 -1
- package/dist/domains/memory/repositories/core-memory-repository.interface.d.ts +92 -0
- package/dist/domains/memory/repositories/core-memory-repository.interface.d.ts.map +1 -0
- package/dist/domains/memory/repositories/core-memory-repository.interface.js +6 -0
- package/dist/domains/memory/repositories/core-memory-repository.interface.js.map +1 -0
- package/dist/domains/memory/repositories/core-memory-repository.js +7 -259
- package/dist/domains/memory/repositories/core-memory-repository.js.map +1 -1
- package/dist/domains/memory/services/core-memory-cache-service.d.ts +55 -0
- package/dist/domains/memory/services/core-memory-cache-service.d.ts.map +1 -1
- package/dist/domains/memory/services/core-memory-cache-service.js +110 -8
- package/dist/domains/memory/services/core-memory-cache-service.js.map +1 -1
- package/dist/domains/memory/services/core-memory-service.d.ts +2 -2
- package/dist/domains/memory/services/core-memory-service.d.ts.map +1 -1
- package/dist/domains/memory/services/core-memory-service.js +43 -24
- package/dist/domains/memory/services/core-memory-service.js.map +1 -1
- package/dist/domains/memory/tools/recall-tool.d.ts.map +1 -1
- package/dist/domains/memory/tools/recall-tool.js +2 -2
- package/dist/domains/memory/tools/recall-tool.js.map +1 -1
- package/dist/domains/memory/tools/remember-tool.d.ts.map +1 -1
- package/dist/domains/memory/tools/remember-tool.js +2 -2
- package/dist/domains/memory/tools/remember-tool.js.map +1 -1
- package/dist/domains/monitoring/services/performance-monitor.d.ts +8 -0
- package/dist/domains/monitoring/services/performance-monitor.d.ts.map +1 -1
- package/dist/domains/monitoring/services/performance-monitor.js +16 -0
- package/dist/domains/monitoring/services/performance-monitor.js.map +1 -1
- package/dist/infrastructure/async-optimizer.d.ts.map +1 -1
- package/dist/infrastructure/async-optimizer.js +8 -7
- package/dist/infrastructure/async-optimizer.js.map +1 -1
- package/dist/infrastructure/database/adapters/sqlite-core-memory-adapter.d.ts +31 -0
- package/dist/infrastructure/database/adapters/sqlite-core-memory-adapter.d.ts.map +1 -0
- package/dist/infrastructure/database/adapters/sqlite-core-memory-adapter.js +112 -0
- package/dist/infrastructure/database/adapters/sqlite-core-memory-adapter.js.map +1 -0
- package/dist/infrastructure/database/database/init.d.ts.map +1 -1
- package/dist/infrastructure/database/database/init.js +23 -2
- package/dist/infrastructure/database/database/init.js.map +1 -1
- package/dist/infrastructure/database/database/migration/migrations/010-add-core-memory-version.d.ts +64 -0
- package/dist/infrastructure/database/database/migration/migrations/010-add-core-memory-version.d.ts.map +1 -0
- package/dist/infrastructure/database/database/migration/migrations/010-add-core-memory-version.js +174 -0
- package/dist/infrastructure/database/database/migration/migrations/010-add-core-memory-version.js.map +1 -0
- package/dist/infrastructure/database/database/migration/migrations/010-add-core-memory-version.sql +25 -0
- package/dist/infrastructure/database/database-lock-monitor.d.ts +105 -0
- package/dist/infrastructure/database/database-lock-monitor.d.ts.map +1 -0
- package/dist/infrastructure/database/database-lock-monitor.js +265 -0
- package/dist/infrastructure/database/database-lock-monitor.js.map +1 -0
- package/dist/infrastructure/database/factories/core-memory-repository.factory.d.ts +17 -0
- package/dist/infrastructure/database/factories/core-memory-repository.factory.d.ts.map +1 -0
- package/dist/infrastructure/database/factories/core-memory-repository.factory.js +43 -0
- package/dist/infrastructure/database/factories/core-memory-repository.factory.js.map +1 -0
- package/dist/infrastructure/database/repositories/core-memory-repository-sqlite.impl.d.ts +63 -0
- package/dist/infrastructure/database/repositories/core-memory-repository-sqlite.impl.d.ts.map +1 -0
- package/dist/infrastructure/database/repositories/core-memory-repository-sqlite.impl.js +281 -0
- package/dist/infrastructure/database/repositories/core-memory-repository-sqlite.impl.js.map +1 -0
- package/dist/infrastructure/database/wal-checkpoint-scheduler.d.ts +166 -0
- package/dist/infrastructure/database/wal-checkpoint-scheduler.d.ts.map +1 -0
- package/dist/infrastructure/database/wal-checkpoint-scheduler.js +285 -0
- package/dist/infrastructure/database/wal-checkpoint-scheduler.js.map +1 -0
- package/dist/npm-client/memento-client.d.ts.map +1 -1
- package/dist/npm-client/memento-client.js +5 -4
- package/dist/npm-client/memento-client.js.map +1 -1
- package/dist/npm-client/memory-manager.d.ts.map +1 -1
- package/dist/npm-client/memory-manager.js +4 -3
- package/dist/npm-client/memory-manager.js.map +1 -1
- package/dist/scripts/check-migration-status.d.ts.map +1 -1
- package/dist/scripts/check-migration-status.js +1 -0
- package/dist/scripts/check-migration-status.js.map +1 -1
- package/dist/server/bootstrap.d.ts +4 -0
- package/dist/server/bootstrap.d.ts.map +1 -1
- package/dist/server/bootstrap.js +27 -2
- package/dist/server/bootstrap.js.map +1 -1
- package/dist/server/http-server.d.ts.map +1 -1
- package/dist/server/http-server.js +59 -31
- package/dist/server/http-server.js.map +1 -1
- package/dist/server/index.js +32 -16
- package/dist/server/index.js.map +1 -1
- package/dist/server/simple-mcp-server.d.ts.map +1 -1
- package/dist/server/simple-mcp-server.js +15 -14
- package/dist/server/simple-mcp-server.js.map +1 -1
- package/dist/shared/config/environment.d.ts.map +1 -1
- package/dist/shared/config/environment.js +13 -1
- package/dist/shared/config/environment.js.map +1 -1
- package/dist/shared/config/index.d.ts.map +1 -1
- package/dist/shared/config/index.js +13 -1
- package/dist/shared/config/index.js.map +1 -1
- package/dist/shared/types/index.d.ts +10 -0
- package/dist/shared/types/index.d.ts.map +1 -1
- package/dist/tools/base-tool.d.ts.map +1 -1
- package/dist/tools/base-tool.js +5 -4
- package/dist/tools/base-tool.js.map +1 -1
- package/dist/tools/migrate-embeddings-tool.d.ts.map +1 -1
- package/dist/tools/migrate-embeddings-tool.js +2 -1
- package/dist/tools/migrate-embeddings-tool.js.map +1 -1
- package/dist/tools/tool-registry.d.ts.map +1 -1
- package/dist/tools/tool-registry.js +4 -3
- package/dist/tools/tool-registry.js.map +1 -1
- package/dist/workers/consolidation-score-worker.d.ts.map +1 -1
- package/dist/workers/consolidation-score-worker.js +5 -4
- package/dist/workers/consolidation-score-worker.js.map +1 -1
- package/package.json +1 -1
- package/scripts/check-file-sizes.ts +405 -0
- package/scripts/count-any-types.ts +375 -0
- package/scripts/count-console-logs.ts +451 -0
package/dist/infrastructure/database/database/migration/migrations/010-add-core-memory-version.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration: 010 - Add Core Memory Version Column
|
|
3
|
+
* Description: Add version column to core_memory table for cache invalidation
|
|
4
|
+
* Version: 10.0
|
|
5
|
+
* Date: 2025-12-25
|
|
6
|
+
*/
|
|
7
|
+
import type Database from 'better-sqlite3';
|
|
8
|
+
import type { Migration } from '../types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Add Core Memory Version Column Migration
|
|
11
|
+
*
|
|
12
|
+
* This migration:
|
|
13
|
+
* 1. Adds version column to core_memory table (INTEGER NOT NULL DEFAULT 0)
|
|
14
|
+
* 2. Sets version = 1 for all existing rows
|
|
15
|
+
* 3. Creates index on version column for efficient queries
|
|
16
|
+
*
|
|
17
|
+
* Purpose:
|
|
18
|
+
* - Enable version-based cache invalidation
|
|
19
|
+
* - Track changes to core memory records
|
|
20
|
+
* - Support distributed cache synchronization in the future
|
|
21
|
+
*/
|
|
22
|
+
export declare class AddCoreMemoryVersionMigration implements Migration {
|
|
23
|
+
version: string;
|
|
24
|
+
name: string;
|
|
25
|
+
description: string;
|
|
26
|
+
/**
|
|
27
|
+
* Load SQL file content
|
|
28
|
+
*/
|
|
29
|
+
private loadSQLFile;
|
|
30
|
+
/**
|
|
31
|
+
* Execute SQL script
|
|
32
|
+
* Removes transaction commands (BEGIN TRANSACTION, COMMIT) as MigrationRunner manages transactions
|
|
33
|
+
*/
|
|
34
|
+
private executeSQL;
|
|
35
|
+
/**
|
|
36
|
+
* Check if table exists
|
|
37
|
+
*/
|
|
38
|
+
private tableExists;
|
|
39
|
+
/**
|
|
40
|
+
* Check if index exists
|
|
41
|
+
*/
|
|
42
|
+
private indexExists;
|
|
43
|
+
/**
|
|
44
|
+
* Check if column exists in table
|
|
45
|
+
*/
|
|
46
|
+
private columnExists;
|
|
47
|
+
/**
|
|
48
|
+
* Validate before migration
|
|
49
|
+
*/
|
|
50
|
+
validateBefore(db: Database.Database): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Execute migration (Up)
|
|
53
|
+
*/
|
|
54
|
+
up(db: Database.Database): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Rollback migration (Down)
|
|
57
|
+
*/
|
|
58
|
+
down(db: Database.Database): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Validate after migration
|
|
61
|
+
*/
|
|
62
|
+
validateAfter(db: Database.Database): Promise<void>;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=010-add-core-memory-version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"010-add-core-memory-version.d.ts","sourceRoot":"","sources":["../../../../../../src/infrastructure/database/database/migration/migrations/010-add-core-memory-version.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAI3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAM7C;;;;;;;;;;;;GAYG;AACH,qBAAa,6BAA8B,YAAW,SAAS;IAC7D,OAAO,SAAU;IACjB,IAAI,SAA6B;IACjC,WAAW,SAAoE;IAE/E;;OAEG;IACH,OAAO,CAAC,WAAW;IAKnB;;;OAGG;IACH,OAAO,CAAC,UAAU;IAsBlB;;OAEG;IACH,OAAO,CAAC,WAAW;IAQnB;;OAEG;IACH,OAAO,CAAC,WAAW;IAQnB;;OAEG;IACH,OAAO,CAAC,YAAY;IAKpB;;OAEG;IACG,cAAc,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAe1D;;OAEG;IACG,EAAE,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAS9C;;OAEG;IACG,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BhD;;OAEG;IACG,aAAa,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;CAyC1D"}
|
package/dist/infrastructure/database/database/migration/migrations/010-add-core-memory-version.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration: 010 - Add Core Memory Version Column
|
|
3
|
+
* Description: Add version column to core_memory table for cache invalidation
|
|
4
|
+
* Version: 10.0
|
|
5
|
+
* Date: 2025-12-25
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync } from 'fs';
|
|
8
|
+
import { join, dirname } from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { DependencyValidator } from '../dependency-validator.js';
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
/**
|
|
14
|
+
* Add Core Memory Version Column Migration
|
|
15
|
+
*
|
|
16
|
+
* This migration:
|
|
17
|
+
* 1. Adds version column to core_memory table (INTEGER NOT NULL DEFAULT 0)
|
|
18
|
+
* 2. Sets version = 1 for all existing rows
|
|
19
|
+
* 3. Creates index on version column for efficient queries
|
|
20
|
+
*
|
|
21
|
+
* Purpose:
|
|
22
|
+
* - Enable version-based cache invalidation
|
|
23
|
+
* - Track changes to core memory records
|
|
24
|
+
* - Support distributed cache synchronization in the future
|
|
25
|
+
*/
|
|
26
|
+
export class AddCoreMemoryVersionMigration {
|
|
27
|
+
version = '10.0';
|
|
28
|
+
name = 'add-core-memory-version';
|
|
29
|
+
description = 'Add version column to core_memory table for cache invalidation';
|
|
30
|
+
/**
|
|
31
|
+
* Load SQL file content
|
|
32
|
+
*/
|
|
33
|
+
loadSQLFile(filename) {
|
|
34
|
+
const filePath = join(__dirname, filename);
|
|
35
|
+
return readFileSync(filePath, 'utf-8');
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Execute SQL script
|
|
39
|
+
* Removes transaction commands (BEGIN TRANSACTION, COMMIT) as MigrationRunner manages transactions
|
|
40
|
+
*/
|
|
41
|
+
executeSQL(db, sql) {
|
|
42
|
+
// MigrationRunner가 트랜잭션을 관리하므로 SQL에서 트랜잭션 명령 제거
|
|
43
|
+
let cleanedSQL = sql
|
|
44
|
+
// BEGIN TRANSACTION 제거
|
|
45
|
+
.replace(/BEGIN\s+TRANSACTION\s*;/gi, '')
|
|
46
|
+
// COMMIT 제거
|
|
47
|
+
.replace(/COMMIT\s*;/gi, '')
|
|
48
|
+
// PRAGMA foreign_keys 명령은 유지 (트랜잭션 외부에서도 작동)
|
|
49
|
+
.trim();
|
|
50
|
+
// 빈 줄 제거 및 정리
|
|
51
|
+
cleanedSQL = cleanedSQL
|
|
52
|
+
.split('\n')
|
|
53
|
+
.map(line => line.trim())
|
|
54
|
+
.filter(line => line.length > 0)
|
|
55
|
+
.join('\n');
|
|
56
|
+
if (cleanedSQL.length > 0) {
|
|
57
|
+
db.exec(cleanedSQL);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check if table exists
|
|
62
|
+
*/
|
|
63
|
+
tableExists(db, tableName) {
|
|
64
|
+
const result = db.prepare(`
|
|
65
|
+
SELECT name FROM sqlite_master
|
|
66
|
+
WHERE type='table' AND name=?
|
|
67
|
+
`).get(tableName);
|
|
68
|
+
return !!result;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Check if index exists
|
|
72
|
+
*/
|
|
73
|
+
indexExists(db, indexName) {
|
|
74
|
+
const result = db.prepare(`
|
|
75
|
+
SELECT name FROM sqlite_master
|
|
76
|
+
WHERE type='index' AND name=?
|
|
77
|
+
`).get(indexName);
|
|
78
|
+
return !!result;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Check if column exists in table
|
|
82
|
+
*/
|
|
83
|
+
columnExists(db, tableName, columnName) {
|
|
84
|
+
const columns = db.prepare(`PRAGMA table_info(${tableName})`).all();
|
|
85
|
+
return columns.some(col => col.name === columnName);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Validate before migration
|
|
89
|
+
*/
|
|
90
|
+
async validateBefore(db) {
|
|
91
|
+
// Check if core_memory table exists (required dependency)
|
|
92
|
+
if (!this.tableExists(db, 'core_memory')) {
|
|
93
|
+
throw new Error('core_memory table does not exist. Please run migration 002 first.');
|
|
94
|
+
}
|
|
95
|
+
// Check if version column already exists (should not exist)
|
|
96
|
+
if (this.columnExists(db, 'core_memory', 'version')) {
|
|
97
|
+
throw new Error('version column already exists in core_memory table. Migration may have been already applied.');
|
|
98
|
+
}
|
|
99
|
+
// Note: DependencyValidator는 선택적으로 사용 (테스트 환경에서는 생략 가능)
|
|
100
|
+
// 실제 마이그레이션 실행 시 MigrationRunner가 의존성을 관리함
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Execute migration (Up)
|
|
104
|
+
*/
|
|
105
|
+
async up(db) {
|
|
106
|
+
// Load and execute SQL script
|
|
107
|
+
const sqlScript = this.loadSQLFile('010-add-core-memory-version.sql');
|
|
108
|
+
this.executeSQL(db, sqlScript);
|
|
109
|
+
// Note: Schema version is recorded by MigrationRunner, not here
|
|
110
|
+
// MigrationRunner.recordVersion() will be called after successful migration
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Rollback migration (Down)
|
|
114
|
+
*/
|
|
115
|
+
async down(db) {
|
|
116
|
+
// Drop index first
|
|
117
|
+
if (this.indexExists(db, 'idx_core_memory_version')) {
|
|
118
|
+
db.exec('DROP INDEX IF EXISTS idx_core_memory_version');
|
|
119
|
+
}
|
|
120
|
+
// SQLite does not support DROP COLUMN directly, so we need to recreate the table
|
|
121
|
+
// This is a simplified rollback - in production, you might want to preserve data
|
|
122
|
+
// For now, we'll just remove the index and log a warning
|
|
123
|
+
// Full rollback would require recreating the table without the version column
|
|
124
|
+
// Note: SQLite does not support ALTER TABLE DROP COLUMN in older versions
|
|
125
|
+
// For a complete rollback, we would need to:
|
|
126
|
+
// 1. Create a new table without version column
|
|
127
|
+
// 2. Copy data (excluding version)
|
|
128
|
+
// 3. Drop old table
|
|
129
|
+
// 4. Rename new table
|
|
130
|
+
// This is complex and risky, so we'll just remove the index for now
|
|
131
|
+
// Remove schema version record (if table exists)
|
|
132
|
+
try {
|
|
133
|
+
if (this.tableExists(db, 'memento_schema_version')) {
|
|
134
|
+
db.prepare('DELETE FROM memento_schema_version WHERE version = ?').run('10.0');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
// 테이블이 없으면 무시 (테스트 환경 등)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Validate after migration
|
|
143
|
+
*/
|
|
144
|
+
async validateAfter(db) {
|
|
145
|
+
// Verify version column exists
|
|
146
|
+
if (!this.columnExists(db, 'core_memory', 'version')) {
|
|
147
|
+
throw new Error('version column was not created in core_memory table');
|
|
148
|
+
}
|
|
149
|
+
// Verify index exists
|
|
150
|
+
if (!this.indexExists(db, 'idx_core_memory_version')) {
|
|
151
|
+
throw new Error('idx_core_memory_version index was not created');
|
|
152
|
+
}
|
|
153
|
+
// Verify no rows have version = 0 (all existing rows should have version = 1)
|
|
154
|
+
const zeroVersionCount = db.prepare(`
|
|
155
|
+
SELECT COUNT(*) as count FROM core_memory WHERE version = 0
|
|
156
|
+
`).get();
|
|
157
|
+
if (zeroVersionCount.count > 0) {
|
|
158
|
+
throw new Error(`Migration validation failed: ${zeroVersionCount.count} rows still have version = 0. All existing rows should have version = 1.`);
|
|
159
|
+
}
|
|
160
|
+
// Verify column type is INTEGER
|
|
161
|
+
const columns = db.prepare(`PRAGMA table_info(core_memory)`).all();
|
|
162
|
+
const versionColumn = columns.find(col => col.name === 'version');
|
|
163
|
+
if (!versionColumn) {
|
|
164
|
+
throw new Error('version column not found in core_memory table');
|
|
165
|
+
}
|
|
166
|
+
if (!versionColumn.type.toUpperCase().includes('INTEGER')) {
|
|
167
|
+
throw new Error(`version column type is ${versionColumn.type}, expected INTEGER`);
|
|
168
|
+
}
|
|
169
|
+
if (versionColumn.notnull !== 1) {
|
|
170
|
+
throw new Error('version column should be NOT NULL');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=010-add-core-memory-version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"010-add-core-memory-version.js","sourceRoot":"","sources":["../../../../../../src/infrastructure/database/database/migration/migrations/010-add-core-memory-version.ts"],"names":[],"mappings":"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"}
|
package/dist/infrastructure/database/database/migration/migrations/010-add-core-memory-version.sql
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- Migration: 010 - Add Core Memory Version Column
|
|
2
|
+
-- Description: Add version column to core_memory table for cache invalidation
|
|
3
|
+
-- Version: 10.0
|
|
4
|
+
-- Date: 2025-12-25
|
|
5
|
+
|
|
6
|
+
-- Core Memory 테이블에 version 컬럼 추가
|
|
7
|
+
-- version 컬럼은 캐시 무효화를 위한 단조 증가하는 버전 번호입니다.
|
|
8
|
+
-- INSERT 시: version = 1
|
|
9
|
+
-- UPDATE 시: version = version + 1
|
|
10
|
+
|
|
11
|
+
PRAGMA foreign_keys = OFF;
|
|
12
|
+
BEGIN TRANSACTION;
|
|
13
|
+
|
|
14
|
+
-- 1. version 컬럼 추가 (기본값 0)
|
|
15
|
+
ALTER TABLE core_memory ADD COLUMN version INTEGER NOT NULL DEFAULT 0;
|
|
16
|
+
|
|
17
|
+
-- 2. 기존 행에 version = 1 설정 (마이그레이션 전 데이터는 초기 버전으로 설정)
|
|
18
|
+
UPDATE core_memory SET version = 1 WHERE version = 0;
|
|
19
|
+
|
|
20
|
+
-- 3. version 컬럼에 대한 인덱스 생성 (버전 기반 쿼리 최적화)
|
|
21
|
+
CREATE INDEX IF NOT EXISTS idx_core_memory_version ON core_memory(version);
|
|
22
|
+
|
|
23
|
+
COMMIT;
|
|
24
|
+
PRAGMA foreign_keys = ON;
|
|
25
|
+
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Lock Monitor
|
|
3
|
+
* SQLite 데이터베이스 락을 주기적으로 모니터링하고 자동으로 해결
|
|
4
|
+
*
|
|
5
|
+
* 클린코드 원칙:
|
|
6
|
+
* - 단일 책임 원칙: 데이터베이스 락 모니터링만 담당
|
|
7
|
+
* - 명확한 인터페이스: LockStatus와 DatabaseLockMonitorConfig로 명확한 타입 정의
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* 락 상태 정보
|
|
11
|
+
*/
|
|
12
|
+
export interface LockStatus {
|
|
13
|
+
/**
|
|
14
|
+
* 락 상태 여부
|
|
15
|
+
*/
|
|
16
|
+
isLocked: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* 락 지속 시간 (밀리초)
|
|
19
|
+
*/
|
|
20
|
+
lockDuration: number;
|
|
21
|
+
/**
|
|
22
|
+
* 락 감지 방법
|
|
23
|
+
* - immediate_transaction: IMMEDIATE 트랜잭션 시도로 감지
|
|
24
|
+
* - busy_timeout: busy_timeout 초과로 감지
|
|
25
|
+
*/
|
|
26
|
+
detectionMethod: 'immediate_transaction' | 'busy_timeout';
|
|
27
|
+
/**
|
|
28
|
+
* busy_timeout 초과 횟수
|
|
29
|
+
*/
|
|
30
|
+
busyCount: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 데이터베이스 락 모니터 설정
|
|
34
|
+
*/
|
|
35
|
+
export interface DatabaseLockMonitorConfig {
|
|
36
|
+
/**
|
|
37
|
+
* 모니터링 주기 (밀리초)
|
|
38
|
+
* 기본값: 1분 (60000ms)
|
|
39
|
+
*/
|
|
40
|
+
intervalMs: number;
|
|
41
|
+
/**
|
|
42
|
+
* 경고 임계값 (밀리초)
|
|
43
|
+
* 기본값: 5초 (5000ms)
|
|
44
|
+
*/
|
|
45
|
+
warningThresholdMs: number;
|
|
46
|
+
/**
|
|
47
|
+
* 위험 임계값 (밀리초)
|
|
48
|
+
* 기본값: 30초 (30000ms)
|
|
49
|
+
*/
|
|
50
|
+
dangerThresholdMs: number;
|
|
51
|
+
/**
|
|
52
|
+
* 치명적 임계값 (밀리초)
|
|
53
|
+
* 기본값: 60초 (60000ms)
|
|
54
|
+
*/
|
|
55
|
+
criticalThresholdMs: number;
|
|
56
|
+
}
|
|
57
|
+
import Database from 'better-sqlite3';
|
|
58
|
+
import type { Logger, PerformanceMonitor } from './wal-checkpoint-scheduler.js';
|
|
59
|
+
import type { WalCheckpointScheduler } from './wal-checkpoint-scheduler.js';
|
|
60
|
+
/**
|
|
61
|
+
* 데이터베이스 락 모니터 클래스
|
|
62
|
+
* SQLite 데이터베이스 락을 주기적으로 모니터링하고 자동으로 해결
|
|
63
|
+
*/
|
|
64
|
+
export declare class DatabaseLockMonitor {
|
|
65
|
+
private db;
|
|
66
|
+
private config;
|
|
67
|
+
private logger?;
|
|
68
|
+
private performanceMonitor?;
|
|
69
|
+
private checkpointScheduler?;
|
|
70
|
+
private intervalId;
|
|
71
|
+
private isRunning;
|
|
72
|
+
private lockStartTime;
|
|
73
|
+
private busyCount;
|
|
74
|
+
private busyEventTimes;
|
|
75
|
+
private statsResetTime;
|
|
76
|
+
constructor(db: Database.Database, config: DatabaseLockMonitorConfig, logger?: Logger | undefined, performanceMonitor?: PerformanceMonitor | undefined, checkpointScheduler?: WalCheckpointScheduler | undefined);
|
|
77
|
+
/**
|
|
78
|
+
* 모니터 시작 (idempotent)
|
|
79
|
+
*/
|
|
80
|
+
start(): void;
|
|
81
|
+
/**
|
|
82
|
+
* 모니터 중지 (idempotent)
|
|
83
|
+
*/
|
|
84
|
+
stop(): void;
|
|
85
|
+
/**
|
|
86
|
+
* 주기적 모니터링
|
|
87
|
+
*/
|
|
88
|
+
private monitor;
|
|
89
|
+
/**
|
|
90
|
+
* busy_timeout 통계 업데이트
|
|
91
|
+
* 시간당 발생 횟수 모니터링
|
|
92
|
+
*/
|
|
93
|
+
private updateBusyStatistics;
|
|
94
|
+
/**
|
|
95
|
+
* 락 상태 확인
|
|
96
|
+
* IMMEDIATE 트랜잭션 시도와 단순 상태 확인 쿼리를 통해 락을 감지
|
|
97
|
+
*/
|
|
98
|
+
private checkLockStatus;
|
|
99
|
+
/**
|
|
100
|
+
* 락 상태 처리
|
|
101
|
+
* 임계값 기반 경고 및 조치 로직
|
|
102
|
+
*/
|
|
103
|
+
private handleLockStatus;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=database-lock-monitor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database-lock-monitor.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/database/database-lock-monitor.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,QAAQ,EAAE,OAAO,CAAC;IAElB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,eAAe,EAAE,uBAAuB,GAAG,cAAc,CAAC;IAE1D;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAE5E;;;GAGG;AACH,qBAAa,mBAAmB;IAS5B,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,MAAM,CAAC;IACf,OAAO,CAAC,kBAAkB,CAAC;IAC3B,OAAO,CAAC,mBAAmB,CAAC;IAZ9B,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,cAAc,CAAgC;gBAG5C,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,MAAM,EAAE,yBAAyB,EACjC,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,kBAAkB,CAAC,EAAE,kBAAkB,YAAA,EACvC,mBAAmB,CAAC,EAAE,sBAAsB,YAAA;IAGtD;;OAEG;IACH,KAAK,IAAI,IAAI;IAcb;;OAEG;IACH,IAAI,IAAI,IAAI;IAmBZ;;OAEG;IACH,OAAO,CAAC,OAAO;IAYf;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IA0B5B;;;OAGG;YACW,eAAe;IAoG7B;;;OAGG;YACW,gBAAgB;CAsE/B"}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Lock Monitor
|
|
3
|
+
* SQLite 데이터베이스 락을 주기적으로 모니터링하고 자동으로 해결
|
|
4
|
+
*
|
|
5
|
+
* 클린코드 원칙:
|
|
6
|
+
* - 단일 책임 원칙: 데이터베이스 락 모니터링만 담당
|
|
7
|
+
* - 명확한 인터페이스: LockStatus와 DatabaseLockMonitorConfig로 명확한 타입 정의
|
|
8
|
+
*/
|
|
9
|
+
import Database from 'better-sqlite3';
|
|
10
|
+
/**
|
|
11
|
+
* 데이터베이스 락 모니터 클래스
|
|
12
|
+
* SQLite 데이터베이스 락을 주기적으로 모니터링하고 자동으로 해결
|
|
13
|
+
*/
|
|
14
|
+
export class DatabaseLockMonitor {
|
|
15
|
+
db;
|
|
16
|
+
config;
|
|
17
|
+
logger;
|
|
18
|
+
performanceMonitor;
|
|
19
|
+
checkpointScheduler;
|
|
20
|
+
intervalId = null;
|
|
21
|
+
isRunning = false;
|
|
22
|
+
lockStartTime = null;
|
|
23
|
+
busyCount = 0;
|
|
24
|
+
busyEventTimes = []; // busy_timeout 발생 시간 기록 (시간당 통계용)
|
|
25
|
+
statsResetTime = Date.now() + 3600000; // 1시간 후 통계 리셋
|
|
26
|
+
constructor(db, config, logger, performanceMonitor, checkpointScheduler) {
|
|
27
|
+
this.db = db;
|
|
28
|
+
this.config = config;
|
|
29
|
+
this.logger = logger;
|
|
30
|
+
this.performanceMonitor = performanceMonitor;
|
|
31
|
+
this.checkpointScheduler = checkpointScheduler;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 모니터 시작 (idempotent)
|
|
35
|
+
*/
|
|
36
|
+
start() {
|
|
37
|
+
if (this.isRunning) {
|
|
38
|
+
this.logger?.warn('데이터베이스 락 모니터가 이미 실행 중입니다');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
this.isRunning = true;
|
|
42
|
+
this.monitor();
|
|
43
|
+
this.logger?.info('데이터베이스 락 모니터 시작됨', {
|
|
44
|
+
intervalMs: this.config.intervalMs
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 모니터 중지 (idempotent)
|
|
49
|
+
*/
|
|
50
|
+
stop() {
|
|
51
|
+
if (!this.isRunning) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (this.intervalId) {
|
|
55
|
+
clearInterval(this.intervalId);
|
|
56
|
+
this.intervalId = null;
|
|
57
|
+
}
|
|
58
|
+
this.isRunning = false;
|
|
59
|
+
this.lockStartTime = null;
|
|
60
|
+
this.busyCount = 0;
|
|
61
|
+
this.busyEventTimes = [];
|
|
62
|
+
this.statsResetTime = Date.now() + 3600000;
|
|
63
|
+
this.logger?.info('데이터베이스 락 모니터 중지됨');
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 주기적 모니터링
|
|
67
|
+
*/
|
|
68
|
+
monitor() {
|
|
69
|
+
this.intervalId = setInterval(async () => {
|
|
70
|
+
try {
|
|
71
|
+
const status = await this.checkLockStatus();
|
|
72
|
+
this.handleLockStatus(status);
|
|
73
|
+
this.updateBusyStatistics();
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
this.logger?.error('락 모니터링 실패', { error });
|
|
77
|
+
}
|
|
78
|
+
}, this.config.intervalMs);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* busy_timeout 통계 업데이트
|
|
82
|
+
* 시간당 발생 횟수 모니터링
|
|
83
|
+
*/
|
|
84
|
+
updateBusyStatistics() {
|
|
85
|
+
const now = Date.now();
|
|
86
|
+
// 1시간이 지났으면 통계 리셋 및 메트릭 기록
|
|
87
|
+
if (now >= this.statsResetTime) {
|
|
88
|
+
const hourlyBusyCount = this.busyEventTimes.length;
|
|
89
|
+
// 시간당 발생 횟수 메트릭 기록
|
|
90
|
+
if (this.performanceMonitor && hourlyBusyCount > 0) {
|
|
91
|
+
this.performanceMonitor.recordMetric('database_lock_hourly_count', hourlyBusyCount);
|
|
92
|
+
// 시간당 발생 횟수가 높으면 경고
|
|
93
|
+
if (hourlyBusyCount > 100) {
|
|
94
|
+
this.logger?.warn('시간당 busy_timeout 발생 횟수가 높음', {
|
|
95
|
+
hourlyBusyCount,
|
|
96
|
+
threshold: 100
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// 통계 리셋
|
|
101
|
+
this.busyEventTimes = [];
|
|
102
|
+
this.statsResetTime = now + 3600000; // 다음 리셋 시간 설정
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 락 상태 확인
|
|
107
|
+
* IMMEDIATE 트랜잭션 시도와 단순 상태 확인 쿼리를 통해 락을 감지
|
|
108
|
+
*/
|
|
109
|
+
async checkLockStatus() {
|
|
110
|
+
// 방법 1: IMMEDIATE 트랜잭션 시도 (관측만 수행, 실제 체크포인트 유발 없음)
|
|
111
|
+
try {
|
|
112
|
+
this.db.prepare('BEGIN IMMEDIATE TRANSACTION').run();
|
|
113
|
+
// 트랜잭션 성공 = 락 없음
|
|
114
|
+
this.db.prepare('ROLLBACK').run();
|
|
115
|
+
// 락이 해제됨
|
|
116
|
+
if (this.lockStartTime) {
|
|
117
|
+
const duration = Date.now() - this.lockStartTime;
|
|
118
|
+
this.lockStartTime = null;
|
|
119
|
+
this.logger?.info('데이터베이스 락 해제됨', { duration });
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
isLocked: false,
|
|
123
|
+
lockDuration: 0,
|
|
124
|
+
detectionMethod: 'immediate_transaction',
|
|
125
|
+
busyCount: this.busyCount
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
if (error.code === 'SQLITE_BUSY') {
|
|
130
|
+
// 락 감지 (IMMEDIATE 트랜잭션 방법)
|
|
131
|
+
this.busyCount++;
|
|
132
|
+
// busy_timeout 발생 시간 기록 (시간당 통계용)
|
|
133
|
+
this.busyEventTimes.push(Date.now());
|
|
134
|
+
// 락 시작 시간 기록
|
|
135
|
+
if (!this.lockStartTime) {
|
|
136
|
+
this.lockStartTime = Date.now();
|
|
137
|
+
}
|
|
138
|
+
// 락 지속 시간 계산
|
|
139
|
+
const lockDuration = this.lockStartTime ? Date.now() - this.lockStartTime : 0;
|
|
140
|
+
return {
|
|
141
|
+
isLocked: true,
|
|
142
|
+
lockDuration,
|
|
143
|
+
detectionMethod: 'immediate_transaction',
|
|
144
|
+
busyCount: this.busyCount
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
// 다른 에러가 발생한 경우, 보조 방법으로 단순 상태 확인 쿼리 시도
|
|
148
|
+
// 방법 2: 단순 상태 확인 쿼리 (읽기 전용, 락 유발 없음)
|
|
149
|
+
try {
|
|
150
|
+
this.db.prepare('SELECT COUNT(*) FROM sqlite_master').get();
|
|
151
|
+
// 쿼리 성공 = 락 없음 (IMMEDIATE 트랜잭션의 다른 에러는 무시)
|
|
152
|
+
if (this.lockStartTime) {
|
|
153
|
+
const duration = Date.now() - this.lockStartTime;
|
|
154
|
+
this.lockStartTime = null;
|
|
155
|
+
this.logger?.info('데이터베이스 락 해제됨', { duration });
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
isLocked: false,
|
|
159
|
+
lockDuration: 0,
|
|
160
|
+
detectionMethod: 'immediate_transaction',
|
|
161
|
+
busyCount: this.busyCount
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
catch (queryError) {
|
|
165
|
+
if (queryError.code === 'SQLITE_BUSY') {
|
|
166
|
+
// 락 감지 (단순 상태 확인 쿼리 방법)
|
|
167
|
+
this.busyCount++;
|
|
168
|
+
// busy_timeout 발생 시간 기록 (시간당 통계용)
|
|
169
|
+
this.busyEventTimes.push(Date.now());
|
|
170
|
+
// 락 시작 시간 기록
|
|
171
|
+
if (!this.lockStartTime) {
|
|
172
|
+
this.lockStartTime = Date.now();
|
|
173
|
+
}
|
|
174
|
+
// 락 지속 시간 계산
|
|
175
|
+
const lockDuration = this.lockStartTime ? Date.now() - this.lockStartTime : 0;
|
|
176
|
+
return {
|
|
177
|
+
isLocked: true,
|
|
178
|
+
lockDuration,
|
|
179
|
+
detectionMethod: 'busy_timeout',
|
|
180
|
+
busyCount: this.busyCount
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
// 두 방법 모두 실패한 경우, 에러 로그만 출력
|
|
184
|
+
this.logger?.error('락 감지 중 예상치 못한 에러 발생', {
|
|
185
|
+
immediateTransactionError: error,
|
|
186
|
+
queryError
|
|
187
|
+
});
|
|
188
|
+
return {
|
|
189
|
+
isLocked: false,
|
|
190
|
+
lockDuration: 0,
|
|
191
|
+
detectionMethod: 'immediate_transaction',
|
|
192
|
+
busyCount: this.busyCount
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* 락 상태 처리
|
|
199
|
+
* 임계값 기반 경고 및 조치 로직
|
|
200
|
+
*/
|
|
201
|
+
async handleLockStatus(status) {
|
|
202
|
+
if (!status.isLocked) {
|
|
203
|
+
return; // 락이 없으면 처리하지 않음
|
|
204
|
+
}
|
|
205
|
+
const { lockDuration } = status;
|
|
206
|
+
// PerformanceMonitor 메트릭 수집
|
|
207
|
+
if (this.performanceMonitor) {
|
|
208
|
+
this.performanceMonitor.incrementCounter('database_lock_count');
|
|
209
|
+
this.performanceMonitor.recordMetric('database_lock_duration', lockDuration);
|
|
210
|
+
}
|
|
211
|
+
// 치명적 임계값 (60초 이상)
|
|
212
|
+
if (lockDuration >= this.config.criticalThresholdMs) {
|
|
213
|
+
this.logger?.warn('데이터베이스 락 치명적 상태 감지', {
|
|
214
|
+
lockDuration,
|
|
215
|
+
detectionMethod: status.detectionMethod,
|
|
216
|
+
busyCount: status.busyCount
|
|
217
|
+
});
|
|
218
|
+
// 체크포인트 시도
|
|
219
|
+
if (this.checkpointScheduler) {
|
|
220
|
+
try {
|
|
221
|
+
await this.checkpointScheduler.checkpointNow();
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
this.logger?.error('체크포인트 실행 실패', { error });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// 에러 로깅
|
|
228
|
+
this.logger?.error('데이터베이스 락 치명적 상태', {
|
|
229
|
+
lockDuration,
|
|
230
|
+
detectionMethod: status.detectionMethod,
|
|
231
|
+
busyCount: status.busyCount
|
|
232
|
+
});
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
// 위험 임계값 (30초 이상)
|
|
236
|
+
if (lockDuration >= this.config.dangerThresholdMs) {
|
|
237
|
+
this.logger?.warn('데이터베이스 락 위험 상태 감지', {
|
|
238
|
+
lockDuration,
|
|
239
|
+
detectionMethod: status.detectionMethod,
|
|
240
|
+
busyCount: status.busyCount
|
|
241
|
+
});
|
|
242
|
+
// 체크포인트 시도
|
|
243
|
+
if (this.checkpointScheduler) {
|
|
244
|
+
try {
|
|
245
|
+
await this.checkpointScheduler.checkpointNow();
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
this.logger?.error('체크포인트 실행 실패', { error });
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
// 경고 임계값 (5초 이상)
|
|
254
|
+
if (lockDuration >= this.config.warningThresholdMs) {
|
|
255
|
+
this.logger?.warn('데이터베이스 락 경고 상태 감지', {
|
|
256
|
+
lockDuration,
|
|
257
|
+
detectionMethod: status.detectionMethod,
|
|
258
|
+
busyCount: status.busyCount
|
|
259
|
+
});
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
// 임계값 미만이면 로그 출력하지 않음
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
//# sourceMappingURL=database-lock-monitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database-lock-monitor.js","sourceRoot":"","sources":["../../../src/infrastructure/database/database-lock-monitor.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA0DH,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAItC;;;GAGG;AACH,MAAM,OAAO,mBAAmB;IASpB;IACA;IACA;IACA;IACA;IAZF,UAAU,GAA0B,IAAI,CAAC;IACzC,SAAS,GAAY,KAAK,CAAC;IAC3B,aAAa,GAAkB,IAAI,CAAC;IACpC,SAAS,GAAW,CAAC,CAAC;IACtB,cAAc,GAAa,EAAE,CAAC,CAAC,kCAAkC;IACjE,cAAc,GAAW,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,cAAc;IAErE,YACU,EAAqB,EACrB,MAAiC,EACjC,MAAe,EACf,kBAAuC,EACvC,mBAA4C;QAJ5C,OAAE,GAAF,EAAE,CAAmB;QACrB,WAAM,GAAN,MAAM,CAA2B;QACjC,WAAM,GAAN,MAAM,CAAS;QACf,uBAAkB,GAAlB,kBAAkB,CAAqB;QACvC,wBAAmB,GAAnB,mBAAmB,CAAyB;IACnD,CAAC;IAEJ;;OAEG;IACH,KAAK;QACH,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,0BAA0B,CAAC,CAAC;YAC9C,OAAO;QACT,CAAC;QAED,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,OAAO,EAAE,CAAC;QAEf,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,kBAAkB,EAAE;YACpC,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU;SACnC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,IAAI;QACF,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC/B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;QAED,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACvB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;QACnB,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;QAE3C,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACxC,CAAC;IAED;;OAEG;IACK,OAAO;QACb,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;YACvC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;gBAC5C,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;gBAC9B,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC9B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC7B,CAAC;IAED;;;OAGG;IACK,oBAAoB;QAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEvB,2BAA2B;QAC3B,IAAI,GAAG,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YAC/B,MAAM,eAAe,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC;YAEnD,mBAAmB;YACnB,IAAI,IAAI,CAAC,kBAAkB,IAAI,eAAe,GAAG,CAAC,EAAE,CAAC;gBACnD,IAAI,CAAC,kBAAkB,CAAC,YAAY,CAAC,4BAA4B,EAAE,eAAe,CAAC,CAAC;gBAEpF,oBAAoB;gBACpB,IAAI,eAAe,GAAG,GAAG,EAAE,CAAC;oBAC1B,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,4BAA4B,EAAE;wBAC9C,eAAe;wBACf,SAAS,EAAE,GAAG;qBACf,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,QAAQ;YACR,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC;YACzB,IAAI,CAAC,cAAc,GAAG,GAAG,GAAG,OAAO,CAAC,CAAC,cAAc;QACrD,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,eAAe;QAC3B,mDAAmD;QACnD,IAAI,CAAC;YACH,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAC,GAAG,EAAE,CAAC;YACrD,iBAAiB;YACjB,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,EAAE,CAAC;YAElC,SAAS;YACT,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC;gBACjD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;gBAC1B,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;YAClD,CAAC;YAED,OAAO;gBACL,QAAQ,EAAE,KAAK;gBACf,YAAY,EAAE,CAAC;gBACf,eAAe,EAAE,uBAAuB;gBACxC,SAAS,EAAE,IAAI,CAAC,SAAS;aAC1B,CAAC;QACJ,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;gBACjC,2BAA2B;gBAC3B,IAAI,CAAC,SAAS,EAAE,CAAC;gBAEjB,kCAAkC;gBAClC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;gBAErC,aAAa;gBACb,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;oBACxB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAClC,CAAC;gBAED,aAAa;gBACb,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;gBAE9E,OAAO;oBACL,QAAQ,EAAE,IAAI;oBACd,YAAY;oBACZ,eAAe,EAAE,uBAAuB;oBACxC,SAAS,EAAE,IAAI,CAAC,SAAS;iBAC1B,CAAC;YACJ,CAAC;YAED,wCAAwC;YACxC,qCAAqC;YACrC,IAAI,CAAC;gBACH,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAC,GAAG,EAAE,CAAC;gBAC5D,2CAA2C;gBAC3C,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;oBACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC;oBACjD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;oBAC1B,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;gBAClD,CAAC;gBAED,OAAO;oBACL,QAAQ,EAAE,KAAK;oBACf,YAAY,EAAE,CAAC;oBACf,eAAe,EAAE,uBAAuB;oBACxC,SAAS,EAAE,IAAI,CAAC,SAAS;iBAC1B,CAAC;YACJ,CAAC;YAAC,OAAO,UAAe,EAAE,CAAC;gBACzB,IAAI,UAAU,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;oBACtC,wBAAwB;oBACxB,IAAI,CAAC,SAAS,EAAE,CAAC;oBAEjB,kCAAkC;oBAClC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;oBAErC,aAAa;oBACb,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;wBACxB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;oBAClC,CAAC;oBAED,aAAa;oBACb,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;oBAE9E,OAAO;wBACL,QAAQ,EAAE,IAAI;wBACd,YAAY;wBACZ,eAAe,EAAE,cAAc;wBAC/B,SAAS,EAAE,IAAI,CAAC,SAAS;qBAC1B,CAAC;gBACJ,CAAC;gBAED,4BAA4B;gBAC5B,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,qBAAqB,EAAE;oBACxC,yBAAyB,EAAE,KAAK;oBAChC,UAAU;iBACX,CAAC,CAAC;gBACH,OAAO;oBACL,QAAQ,EAAE,KAAK;oBACf,YAAY,EAAE,CAAC;oBACf,eAAe,EAAE,uBAAuB;oBACxC,SAAS,EAAE,IAAI,CAAC,SAAS;iBAC1B,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,gBAAgB,CAAC,MAAkB;QAC/C,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACrB,OAAO,CAAC,iBAAiB;QAC3B,CAAC;QAED,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC;QAEhC,4BAA4B;QAC5B,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC5B,IAAI,CAAC,kBAAkB,CAAC,gBAAgB,CAAC,qBAAqB,CAAC,CAAC;YAChE,IAAI,CAAC,kBAAkB,CAAC,YAAY,CAAC,wBAAwB,EAAE,YAAY,CAAC,CAAC;QAC/E,CAAC;QAED,mBAAmB;QACnB,IAAI,YAAY,IAAI,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC;YACpD,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE;gBACtC,YAAY;gBACZ,eAAe,EAAE,MAAM,CAAC,eAAe;gBACvC,SAAS,EAAE,MAAM,CAAC,SAAS;aAC5B,CAAC,CAAC;YAEH,WAAW;YACX,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAC7B,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,mBAAmB,CAAC,aAAa,EAAE,CAAC;gBACjD,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC/C,CAAC;YACH,CAAC;YAED,QAAQ;YACR,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,iBAAiB,EAAE;gBACpC,YAAY;gBACZ,eAAe,EAAE,MAAM,CAAC,eAAe;gBACvC,SAAS,EAAE,MAAM,CAAC,SAAS;aAC5B,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,kBAAkB;QAClB,IAAI,YAAY,IAAI,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;YAClD,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,mBAAmB,EAAE;gBACrC,YAAY;gBACZ,eAAe,EAAE,MAAM,CAAC,eAAe;gBACvC,SAAS,EAAE,MAAM,CAAC,SAAS;aAC5B,CAAC,CAAC;YAEH,WAAW;YACX,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAC7B,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,mBAAmB,CAAC,aAAa,EAAE,CAAC;gBACjD,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC/C,CAAC;YACH,CAAC;YACD,OAAO;QACT,CAAC;QAED,iBAAiB;QACjB,IAAI,YAAY,IAAI,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC;YACnD,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,mBAAmB,EAAE;gBACrC,YAAY;gBACZ,eAAe,EAAE,MAAM,CAAC,eAAe;gBACvC,SAAS,EAAE,MAAM,CAAC,SAAS;aAC5B,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,sBAAsB;IACxB,CAAC;CACF"}
|