s3db.js 18.0.11-next.1534f717 → 18.0.11-next.e8e71b5b
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/clients/recker-http-handler.js +56 -8
- package/dist/clients/recker-http-handler.js.map +1 -1
- package/dist/concerns/high-performance-inserter.js +6 -34
- package/dist/concerns/high-performance-inserter.js.map +1 -1
- package/dist/concerns/id/alphabets.js +150 -0
- package/dist/concerns/id/alphabets.js.map +1 -0
- package/dist/concerns/id/entropy.js +243 -0
- package/dist/concerns/id/entropy.js.map +1 -0
- package/dist/concerns/id/generators/nanoid.js +74 -0
- package/dist/concerns/id/generators/nanoid.js.map +1 -0
- package/dist/concerns/id/generators/sid.js +73 -0
- package/dist/concerns/id/generators/sid.js.map +1 -0
- package/dist/concerns/id/generators/ulid.js +208 -0
- package/dist/concerns/id/generators/ulid.js.map +1 -0
- package/dist/concerns/id/generators/uuid-v7.js +150 -0
- package/dist/concerns/id/generators/uuid-v7.js.map +1 -0
- package/dist/concerns/id/index.js +74 -0
- package/dist/concerns/id/index.js.map +1 -0
- package/dist/concerns/plugin-storage.js +114 -0
- package/dist/concerns/plugin-storage.js.map +1 -1
- package/dist/concerns/s3-errors.js +72 -0
- package/dist/concerns/s3-errors.js.map +1 -0
- package/dist/concerns/s3-key.js +54 -0
- package/dist/concerns/s3-key.js.map +1 -0
- package/dist/concerns/safe-merge.js +47 -0
- package/dist/concerns/safe-merge.js.map +1 -0
- package/dist/core/resource-config-validator.js +12 -2
- package/dist/core/resource-config-validator.js.map +1 -1
- package/dist/core/resource-partitions.class.js +12 -1
- package/dist/core/resource-partitions.class.js.map +1 -1
- package/dist/core/resource-persistence.class.js +41 -12
- package/dist/core/resource-persistence.class.js.map +1 -1
- package/dist/core/resource-query.class.js +21 -47
- package/dist/core/resource-query.class.js.map +1 -1
- package/dist/database/database-connection.class.js +3 -6
- package/dist/database/database-connection.class.js.map +1 -1
- package/dist/database/database-plugins.class.js +7 -13
- package/dist/database/database-plugins.class.js.map +1 -1
- package/dist/plugins/concerns/s3-mutex.class.js +155 -0
- package/dist/plugins/concerns/s3-mutex.class.js.map +1 -0
- package/dist/plugins/eventual-consistency/consolidation.js +4 -7
- package/dist/plugins/eventual-consistency/consolidation.js.map +1 -1
- package/dist/plugins/eventual-consistency/garbage-collection.js +3 -6
- package/dist/plugins/eventual-consistency/garbage-collection.js.map +1 -1
- package/dist/plugins/queue-consumer.plugin.js +10 -16
- package/dist/plugins/queue-consumer.plugin.js.map +1 -1
- package/dist/plugins/recon/managers/scheduler-manager.js +3 -5
- package/dist/plugins/recon/managers/scheduler-manager.js.map +1 -1
- package/dist/plugins/recon/stages/recker-asn-stage.js +279 -0
- package/dist/plugins/recon/stages/recker-asn-stage.js.map +1 -0
- package/dist/plugins/recon/stages/recker-dns-stage.js +227 -0
- package/dist/plugins/recon/stages/recker-dns-stage.js.map +1 -0
- package/dist/plugins/recon/stages/recker-scrape-stage.js +369 -0
- package/dist/plugins/recon/stages/recker-scrape-stage.js.map +1 -0
- package/dist/plugins/replicator.plugin.js +13 -31
- package/dist/plugins/replicator.plugin.js.map +1 -1
- package/dist/plugins/replicators/base-replicator.class.js +10 -23
- package/dist/plugins/replicators/base-replicator.class.js.map +1 -1
- package/dist/plugins/spider/recker-link-discoverer.js +544 -0
- package/dist/plugins/spider/recker-link-discoverer.js.map +1 -0
- package/dist/plugins/spider/recker-llms-validator.js +334 -0
- package/dist/plugins/spider/recker-llms-validator.js.map +1 -0
- package/dist/plugins/spider/recker-robots-validator.js +336 -0
- package/dist/plugins/spider/recker-robots-validator.js.map +1 -0
- package/dist/plugins/spider/recker-security-adapter.js +325 -0
- package/dist/plugins/spider/recker-security-adapter.js.map +1 -0
- package/dist/plugins/spider/recker-seo-adapter.js +399 -0
- package/dist/plugins/spider/recker-seo-adapter.js.map +1 -0
- package/dist/plugins/spider/recker-sitemap-validator.js +406 -0
- package/dist/plugins/spider/recker-sitemap-validator.js.map +1 -0
- package/dist/resource.class.js +2 -0
- package/dist/resource.class.js.map +1 -1
- package/dist/s3db.cjs +444 -219
- package/dist/s3db.cjs.map +1 -1
- package/dist/s3db.es.js +445 -220
- package/dist/s3db.es.js.map +1 -1
- package/dist/stream/resource-reader.class.js +5 -7
- package/dist/stream/resource-reader.class.js.map +1 -1
- package/dist/stream/resource-writer.class.js +5 -7
- package/dist/stream/resource-writer.class.js.map +1 -1
- package/dist/tasks/tasks-pool.class.js +31 -0
- package/dist/tasks/tasks-pool.class.js.map +1 -1
- package/dist/types/clients/recker-http-handler.d.ts +1 -0
- package/dist/types/clients/recker-http-handler.d.ts.map +1 -1
- package/dist/types/clients/types.d.ts +14 -0
- package/dist/types/clients/types.d.ts.map +1 -1
- package/dist/types/concerns/high-performance-inserter.d.ts.map +1 -1
- package/dist/types/concerns/id/alphabets.d.ts +125 -0
- package/dist/types/concerns/id/alphabets.d.ts.map +1 -0
- package/dist/types/concerns/id/entropy.d.ts +84 -0
- package/dist/types/concerns/id/entropy.d.ts.map +1 -0
- package/dist/types/concerns/id/generators/nanoid.d.ts +46 -0
- package/dist/types/concerns/id/generators/nanoid.d.ts.map +1 -0
- package/dist/types/concerns/id/generators/sid.d.ts +45 -0
- package/dist/types/concerns/id/generators/sid.d.ts.map +1 -0
- package/dist/types/concerns/id/generators/ulid.d.ts +71 -0
- package/dist/types/concerns/id/generators/ulid.d.ts.map +1 -0
- package/dist/types/concerns/id/generators/uuid-v7.d.ts +60 -0
- package/dist/types/concerns/id/generators/uuid-v7.d.ts.map +1 -0
- package/dist/types/concerns/id/index.d.ts +51 -0
- package/dist/types/concerns/id/index.d.ts.map +1 -0
- package/dist/types/concerns/plugin-storage.d.ts +25 -0
- package/dist/types/concerns/plugin-storage.d.ts.map +1 -1
- package/dist/types/concerns/s3-errors.d.ts +20 -0
- package/dist/types/concerns/s3-errors.d.ts.map +1 -0
- package/dist/types/concerns/s3-key.d.ts +30 -0
- package/dist/types/concerns/s3-key.d.ts.map +1 -0
- package/dist/types/concerns/safe-merge.d.ts +22 -0
- package/dist/types/concerns/safe-merge.d.ts.map +1 -0
- package/dist/types/core/resource-config-validator.d.ts.map +1 -1
- package/dist/types/core/resource-partitions.class.d.ts.map +1 -1
- package/dist/types/core/resource-persistence.class.d.ts.map +1 -1
- package/dist/types/core/resource-query.class.d.ts.map +1 -1
- package/dist/types/database/database-connection.class.d.ts.map +1 -1
- package/dist/types/database/database-plugins.class.d.ts.map +1 -1
- package/dist/types/plugins/concerns/s3-mutex.class.d.ts +30 -0
- package/dist/types/plugins/concerns/s3-mutex.class.d.ts.map +1 -0
- package/dist/types/plugins/eventual-consistency/consolidation.d.ts.map +1 -1
- package/dist/types/plugins/eventual-consistency/garbage-collection.d.ts.map +1 -1
- package/dist/types/plugins/queue-consumer.plugin.d.ts.map +1 -1
- package/dist/types/plugins/recon/managers/scheduler-manager.d.ts.map +1 -1
- package/dist/types/plugins/recon/stages/recker-asn-stage.d.ts +90 -0
- package/dist/types/plugins/recon/stages/recker-asn-stage.d.ts.map +1 -0
- package/dist/types/plugins/recon/stages/recker-dns-stage.d.ts +125 -0
- package/dist/types/plugins/recon/stages/recker-dns-stage.d.ts.map +1 -0
- package/dist/types/plugins/recon/stages/recker-scrape-stage.d.ts +96 -0
- package/dist/types/plugins/recon/stages/recker-scrape-stage.d.ts.map +1 -0
- package/dist/types/plugins/replicator.plugin.d.ts.map +1 -1
- package/dist/types/plugins/replicators/base-replicator.class.d.ts.map +1 -1
- package/dist/types/plugins/spider/recker-link-discoverer.d.ts +54 -0
- package/dist/types/plugins/spider/recker-link-discoverer.d.ts.map +1 -0
- package/dist/types/plugins/spider/recker-llms-validator.d.ts +105 -0
- package/dist/types/plugins/spider/recker-llms-validator.d.ts.map +1 -0
- package/dist/types/plugins/spider/recker-robots-validator.d.ts +92 -0
- package/dist/types/plugins/spider/recker-robots-validator.d.ts.map +1 -0
- package/dist/types/plugins/spider/recker-security-adapter.d.ts +83 -0
- package/dist/types/plugins/spider/recker-security-adapter.d.ts.map +1 -0
- package/dist/types/plugins/spider/recker-seo-adapter.d.ts +187 -0
- package/dist/types/plugins/spider/recker-seo-adapter.d.ts.map +1 -0
- package/dist/types/plugins/spider/recker-sitemap-validator.d.ts +121 -0
- package/dist/types/plugins/spider/recker-sitemap-validator.d.ts.map +1 -0
- package/dist/types/resource.class.d.ts.map +1 -1
- package/dist/types/stream/resource-reader.class.d.ts.map +1 -1
- package/dist/types/stream/resource-writer.class.d.ts.map +1 -1
- package/dist/types/tasks/tasks-pool.class.d.ts +23 -0
- package/dist/types/tasks/tasks-pool.class.d.ts.map +1 -1
- package/mcp/prompts/index.ts +275 -0
- package/mcp/resources/index.ts +322 -0
- package/mcp/tools/plugins.ts +1137 -0
- package/mcp/tools/streams.ts +340 -0
- package/package.json +20 -22
- package/src/clients/recker-http-handler.ts +74 -8
- package/src/clients/types.ts +14 -0
- package/src/concerns/high-performance-inserter.ts +18 -57
- package/src/concerns/id/alphabets.ts +175 -0
- package/src/concerns/id/entropy.ts +286 -0
- package/src/concerns/id/generators/sid.ts +90 -0
- package/src/concerns/id/generators/ulid.ts +249 -0
- package/src/concerns/id/generators/uuid-v7.ts +179 -0
- package/src/concerns/id/index.ts +167 -0
- package/src/concerns/plugin-storage.ts +144 -0
- package/src/concerns/s3-errors.ts +97 -0
- package/src/concerns/s3-key.ts +62 -0
- package/src/concerns/safe-merge.ts +60 -0
- package/src/core/resource-config-validator.ts +9 -2
- package/src/core/resource-partitions.class.ts +14 -1
- package/src/core/resource-persistence.class.ts +47 -13
- package/src/core/resource-query.class.ts +21 -46
- package/src/database/database-connection.class.ts +7 -6
- package/src/database/database-plugins.class.ts +15 -13
- package/src/plugins/concerns/s3-mutex.class.ts +228 -0
- package/src/plugins/eventual-consistency/consolidation.ts +8 -7
- package/src/plugins/eventual-consistency/garbage-collection.ts +7 -6
- package/src/plugins/queue-consumer.plugin.ts +21 -19
- package/src/plugins/recon/managers/scheduler-manager.ts +7 -5
- package/src/plugins/recon/stages/recker-asn-stage.ts +385 -0
- package/src/plugins/recon/stages/recker-dns-stage.ts +360 -0
- package/src/plugins/recon/stages/recker-scrape-stage.ts +509 -0
- package/src/plugins/replicator.plugin.ts +41 -35
- package/src/plugins/replicators/base-replicator.class.ts +17 -23
- package/src/plugins/spider/recker-link-discoverer.ts +645 -0
- package/src/plugins/spider/recker-llms-validator.ts +500 -0
- package/src/plugins/spider/recker-robots-validator.ts +473 -0
- package/src/plugins/spider/recker-security-adapter.ts +489 -0
- package/src/plugins/spider/recker-seo-adapter.ts +605 -0
- package/src/plugins/spider/recker-sitemap-validator.ts +621 -0
- package/src/resource.class.ts +2 -0
- package/src/stream/resource-reader.class.ts +10 -8
- package/src/stream/resource-writer.class.ts +10 -8
- package/src/tasks/tasks-pool.class.ts +46 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TasksPool } from '../tasks/tasks-pool.class.js';
|
|
2
2
|
import Resource from '../resource.class.js';
|
|
3
3
|
import tryFn, { tryFnSync } from '../concerns/try-fn.js';
|
|
4
4
|
import { streamToString } from '../stream/index.js';
|
|
@@ -182,16 +182,17 @@ export class DatabaseConnection {
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
const stopConcurrency = Math.max(1, Number.isFinite(db.executorPool?.concurrency) ? db.executorPool.concurrency! : 5);
|
|
185
|
-
await
|
|
186
|
-
.
|
|
187
|
-
|
|
188
|
-
.process(async (plugin) => {
|
|
185
|
+
await TasksPool.map(
|
|
186
|
+
db.pluginList,
|
|
187
|
+
async (plugin) => {
|
|
189
188
|
await tryFn(async () => {
|
|
190
189
|
if (plugin && typeof (plugin as any).stop === 'function') {
|
|
191
190
|
await (plugin as any).stop();
|
|
192
191
|
}
|
|
193
192
|
});
|
|
194
|
-
}
|
|
193
|
+
},
|
|
194
|
+
{ concurrency: stopConcurrency }
|
|
195
|
+
);
|
|
195
196
|
}
|
|
196
197
|
|
|
197
198
|
if (db.resources && Object.keys(db.resources).length > 0) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isEmpty, isFunction } from 'lodash-es';
|
|
2
|
-
import {
|
|
2
|
+
import { TasksPool } from '../tasks/tasks-pool.class.js';
|
|
3
3
|
import { DatabaseError } from '../errors.js';
|
|
4
4
|
import type { DatabaseRef, Plugin, PluginConstructor, MemorySnapshot } from './types.js';
|
|
5
5
|
import type { DatabaseCoordinators } from './database-coordinators.class.js';
|
|
@@ -31,10 +31,9 @@ export class DatabasePlugins {
|
|
|
31
31
|
|
|
32
32
|
const concurrency = Math.max(1, Number.isFinite(db.executorPool?.concurrency) ? db.executorPool.concurrency! : 5);
|
|
33
33
|
|
|
34
|
-
const installResult = await
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
.process(async (plugin) => {
|
|
34
|
+
const installResult = await TasksPool.map(
|
|
35
|
+
plugins,
|
|
36
|
+
async (plugin) => {
|
|
38
37
|
const pluginName = this._getPluginName(plugin);
|
|
39
38
|
|
|
40
39
|
if (typeof plugin.setInstanceName === 'function') {
|
|
@@ -53,12 +52,14 @@ export class DatabasePlugins {
|
|
|
53
52
|
|
|
54
53
|
db.pluginRegistry[pluginName] = plugin;
|
|
55
54
|
return pluginName;
|
|
56
|
-
}
|
|
55
|
+
},
|
|
56
|
+
{ concurrency }
|
|
57
|
+
);
|
|
57
58
|
|
|
58
59
|
if (installResult.errors.length > 0) {
|
|
59
60
|
const errorInfo = installResult.errors[0]!;
|
|
60
61
|
const failedPlugin = errorInfo.item;
|
|
61
|
-
const error =
|
|
62
|
+
const error = errorInfo.error;
|
|
62
63
|
const failedName = this._getPluginName(failedPlugin);
|
|
63
64
|
throw new DatabaseError(`Failed to install plugin '${failedName}': ${error?.message || error}`, {
|
|
64
65
|
operation: 'startPlugins.install',
|
|
@@ -67,10 +68,9 @@ export class DatabasePlugins {
|
|
|
67
68
|
});
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
const startResult = await
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
.process(async (plugin) => {
|
|
71
|
+
const startResult = await TasksPool.map(
|
|
72
|
+
plugins,
|
|
73
|
+
async (plugin) => {
|
|
74
74
|
const pluginName = this._getPluginName(plugin);
|
|
75
75
|
await plugin.start();
|
|
76
76
|
db.emit('db:plugin:metrics', {
|
|
@@ -79,12 +79,14 @@ export class DatabasePlugins {
|
|
|
79
79
|
...this.coordinators.collectMemorySnapshot()
|
|
80
80
|
});
|
|
81
81
|
return plugin;
|
|
82
|
-
}
|
|
82
|
+
},
|
|
83
|
+
{ concurrency }
|
|
84
|
+
);
|
|
83
85
|
|
|
84
86
|
if (startResult.errors.length > 0) {
|
|
85
87
|
const errorInfo = startResult.errors[0]!;
|
|
86
88
|
const failedPlugin = errorInfo.item;
|
|
87
|
-
const error =
|
|
89
|
+
const error = errorInfo.error;
|
|
88
90
|
const failedName = this._getPluginName(failedPlugin);
|
|
89
91
|
throw new DatabaseError(`Failed to start plugin '${failedName}': ${error?.message || error}`, {
|
|
90
92
|
operation: 'startPlugins.start',
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { PluginStorage } from '../../concerns/plugin-storage.js';
|
|
2
|
+
|
|
3
|
+
export interface LockResult {
|
|
4
|
+
acquired: boolean;
|
|
5
|
+
lockId?: string;
|
|
6
|
+
expiresAt?: number;
|
|
7
|
+
error?: Error;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface LockData {
|
|
11
|
+
lockId: string;
|
|
12
|
+
holderId: string;
|
|
13
|
+
acquiredAt: number;
|
|
14
|
+
expiresAt: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const DEFAULT_TTL_MS = 30000;
|
|
18
|
+
const DEFAULT_NAMESPACE = 'default';
|
|
19
|
+
|
|
20
|
+
export class S3Mutex {
|
|
21
|
+
protected storage: PluginStorage;
|
|
22
|
+
protected namespace: string;
|
|
23
|
+
protected holderId: string;
|
|
24
|
+
|
|
25
|
+
constructor(storage: PluginStorage, namespace?: string) {
|
|
26
|
+
if (!storage) {
|
|
27
|
+
throw new Error('S3Mutex: storage is required');
|
|
28
|
+
}
|
|
29
|
+
this.storage = storage;
|
|
30
|
+
this.namespace = namespace || DEFAULT_NAMESPACE;
|
|
31
|
+
this.holderId = this._generateHolderId();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async lock(key: string, ttlMs: number = DEFAULT_TTL_MS): Promise<LockResult> {
|
|
35
|
+
return this.tryLock(key, ttlMs);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async tryLock(key: string, ttlMs: number = DEFAULT_TTL_MS): Promise<LockResult> {
|
|
39
|
+
if (!key) {
|
|
40
|
+
return {
|
|
41
|
+
acquired: false,
|
|
42
|
+
error: new Error('S3Mutex: key is required')
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const lockKey = this._getLockKey(key);
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
const expiresAt = now + ttlMs;
|
|
49
|
+
const lockId = this._generateLockId();
|
|
50
|
+
|
|
51
|
+
const lockData: LockData = {
|
|
52
|
+
lockId,
|
|
53
|
+
holderId: this.holderId,
|
|
54
|
+
acquiredAt: now,
|
|
55
|
+
expiresAt
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const version = await this.storage.setIfNotExists(
|
|
59
|
+
lockKey,
|
|
60
|
+
lockData as unknown as Record<string, unknown>,
|
|
61
|
+
{ ttl: Math.ceil(ttlMs / 1000) + 60, behavior: 'body-only' }
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
if (version !== null) {
|
|
65
|
+
return {
|
|
66
|
+
acquired: true,
|
|
67
|
+
lockId,
|
|
68
|
+
expiresAt
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const existingResult = await this.storage.getWithVersion(lockKey);
|
|
73
|
+
|
|
74
|
+
if (!existingResult.data) {
|
|
75
|
+
return {
|
|
76
|
+
acquired: false,
|
|
77
|
+
error: new Error('Lock exists but could not be read')
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const existingLock = existingResult.data as unknown as LockData;
|
|
82
|
+
|
|
83
|
+
if (existingLock.expiresAt <= now) {
|
|
84
|
+
const newVersion = await this.storage.setIfVersion(
|
|
85
|
+
lockKey,
|
|
86
|
+
lockData as unknown as Record<string, unknown>,
|
|
87
|
+
existingResult.version!,
|
|
88
|
+
{ ttl: Math.ceil(ttlMs / 1000) + 60, behavior: 'body-only' }
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (newVersion !== null) {
|
|
92
|
+
return {
|
|
93
|
+
acquired: true,
|
|
94
|
+
lockId,
|
|
95
|
+
expiresAt
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
acquired: false,
|
|
101
|
+
error: new Error('Lock was taken by another process during expired lock takeover')
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
acquired: false,
|
|
107
|
+
error: new Error(`Lock is held by ${existingLock.holderId} until ${new Date(existingLock.expiresAt).toISOString()}`)
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async unlock(key: string, lockId: string): Promise<boolean> {
|
|
112
|
+
if (!key || !lockId) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const lockKey = this._getLockKey(key);
|
|
117
|
+
|
|
118
|
+
const result = await this.storage.getWithVersion(lockKey);
|
|
119
|
+
|
|
120
|
+
if (!result.data || !result.version) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const existingLock = result.data as unknown as LockData;
|
|
125
|
+
|
|
126
|
+
if (existingLock.lockId !== lockId) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return await this.storage.deleteIfVersion(lockKey, result.version);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async isLocked(key: string): Promise<boolean> {
|
|
134
|
+
if (!key) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const lockKey = this._getLockKey(key);
|
|
139
|
+
const result = await this.storage.getWithVersion(lockKey);
|
|
140
|
+
|
|
141
|
+
if (!result.data) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const lockData = result.data as unknown as LockData;
|
|
146
|
+
const now = Date.now();
|
|
147
|
+
|
|
148
|
+
return lockData.expiresAt > now;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async extend(key: string, lockId: string, ttlMs: number): Promise<boolean> {
|
|
152
|
+
if (!key || !lockId || ttlMs <= 0) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const lockKey = this._getLockKey(key);
|
|
157
|
+
const result = await this.storage.getWithVersion(lockKey);
|
|
158
|
+
|
|
159
|
+
if (!result.data || !result.version) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const existingLock = result.data as unknown as LockData;
|
|
164
|
+
const now = Date.now();
|
|
165
|
+
|
|
166
|
+
if (existingLock.lockId !== lockId) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (existingLock.expiresAt <= now) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const newExpiresAt = now + ttlMs;
|
|
175
|
+
const updatedLock: LockData = {
|
|
176
|
+
...existingLock,
|
|
177
|
+
expiresAt: newExpiresAt
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const newVersion = await this.storage.setIfVersion(
|
|
181
|
+
lockKey,
|
|
182
|
+
updatedLock as unknown as Record<string, unknown>,
|
|
183
|
+
result.version,
|
|
184
|
+
{ ttl: Math.ceil(ttlMs / 1000) + 60, behavior: 'body-only' }
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
return newVersion !== null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async getLockInfo(key: string): Promise<LockData | null> {
|
|
191
|
+
if (!key) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const lockKey = this._getLockKey(key);
|
|
196
|
+
const result = await this.storage.getWithVersion(lockKey);
|
|
197
|
+
|
|
198
|
+
if (!result.data) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return result.data as unknown as LockData;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
protected _getLockKey(key: string): string {
|
|
206
|
+
return this.storage.getPluginKey(null, 'locks', `namespace=${this.namespace}`, `${key}.json`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
protected _generateLockId(): string {
|
|
210
|
+
const timestamp = Date.now();
|
|
211
|
+
const random = Math.random().toString(36).substring(2, 10);
|
|
212
|
+
return `lock-${this.holderId}-${timestamp}-${random}`;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
protected _generateHolderId(): string {
|
|
216
|
+
if (typeof process !== 'undefined' && process.env) {
|
|
217
|
+
if (process.env.POD_NAME) {
|
|
218
|
+
return `holder-${process.env.POD_NAME}`;
|
|
219
|
+
}
|
|
220
|
+
if (process.env.HOSTNAME) {
|
|
221
|
+
return `holder-${process.env.HOSTNAME}`;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return `holder-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export default S3Mutex;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import tryFn from '../../concerns/try-fn.js';
|
|
7
|
-
import {
|
|
7
|
+
import { TasksPool } from '../../tasks/tasks-pool.class.js';
|
|
8
8
|
import { getCronManager } from '../../concerns/cron-manager.js';
|
|
9
9
|
import { createLogger } from '../../concerns/logger.js';
|
|
10
10
|
import { PluginError } from '../../errors.js';
|
|
@@ -141,17 +141,18 @@ export async function runConsolidation(
|
|
|
141
141
|
|
|
142
142
|
const byOriginalId = groupByOriginalId(allTransactions);
|
|
143
143
|
|
|
144
|
-
const { results, errors } = await
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
.process(async ([originalId, transactions]) => {
|
|
144
|
+
const { results, errors } = await TasksPool.map(
|
|
145
|
+
Object.entries(byOriginalId),
|
|
146
|
+
async ([originalId, transactions]) => {
|
|
148
147
|
return consolidateRecord(
|
|
149
148
|
handler,
|
|
150
149
|
originalId,
|
|
151
150
|
transactions,
|
|
152
151
|
config
|
|
153
152
|
);
|
|
154
|
-
}
|
|
153
|
+
},
|
|
154
|
+
{ concurrency: 10 }
|
|
155
|
+
);
|
|
155
156
|
|
|
156
157
|
for (const recordResult of results) {
|
|
157
158
|
if (recordResult) {
|
|
@@ -160,7 +161,7 @@ export async function runConsolidation(
|
|
|
160
161
|
}
|
|
161
162
|
}
|
|
162
163
|
|
|
163
|
-
result.errors = errors;
|
|
164
|
+
result.errors = errors.map(e => e.error);
|
|
164
165
|
result.success = errors.length === 0;
|
|
165
166
|
|
|
166
167
|
if (config.enableAnalytics && handler.analyticsResource) {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import tryFn from '../../concerns/try-fn.js';
|
|
7
|
-
import {
|
|
7
|
+
import { TasksPool } from '../../tasks/tasks-pool.class.js';
|
|
8
8
|
import { getCronManager } from '../../concerns/cron-manager.js';
|
|
9
9
|
import type { FieldHandler, Transaction } from './utils.js';
|
|
10
10
|
import type { NormalizedConfig } from './config.js';
|
|
@@ -105,13 +105,14 @@ export async function runGarbageCollection(
|
|
|
105
105
|
return;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
const { results, errors } = await
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
.process(async (txn) => {
|
|
108
|
+
const { results, errors } = await TasksPool.map(
|
|
109
|
+
oldTransactions,
|
|
110
|
+
async (txn) => {
|
|
112
111
|
const [deleted] = await tryFn(() => transactionResource!.delete(txn.id));
|
|
113
112
|
return deleted;
|
|
114
|
-
}
|
|
113
|
+
},
|
|
114
|
+
{ concurrency: 10 }
|
|
115
|
+
);
|
|
115
116
|
|
|
116
117
|
if (emitFn) {
|
|
117
118
|
emitFn('plg:eventual-consistency:gc-completed', {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TasksPool } from '../tasks/tasks-pool.class.js';
|
|
2
2
|
import { Plugin } from './plugin.class.js';
|
|
3
3
|
import { createConsumer } from './consumers/index.js';
|
|
4
4
|
import tryFn from '../concerns/try-fn.js';
|
|
@@ -124,20 +124,21 @@ export class QueueConsumerPlugin extends Plugin {
|
|
|
124
124
|
return;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
const { errors } = await
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
.process(async (task: StartTask) => {
|
|
127
|
+
const { errors } = await TasksPool.map(
|
|
128
|
+
startTasks,
|
|
129
|
+
async (task: StartTask) => {
|
|
131
130
|
await task.start();
|
|
132
131
|
return `${task.driver}:${task.resource}`;
|
|
133
|
-
}
|
|
132
|
+
},
|
|
133
|
+
{ concurrency: this.startConcurrency }
|
|
134
|
+
);
|
|
134
135
|
|
|
135
136
|
if (errors.length > 0) {
|
|
136
|
-
const messages = errors.map((
|
|
137
|
-
const task =
|
|
138
|
-
const reason =
|
|
137
|
+
const messages = errors.map((errorInfo) => {
|
|
138
|
+
const task = errorInfo.item as StartTask | undefined;
|
|
139
|
+
const reason = errorInfo.error;
|
|
139
140
|
const identifier = task ? `${task.driver || 'unknown'}:${task.resource || 'unknown'}` : 'unknown';
|
|
140
|
-
return `[${identifier}] ${
|
|
141
|
+
return `[${identifier}] ${reason?.message || reason}`;
|
|
141
142
|
});
|
|
142
143
|
|
|
143
144
|
throw new QueueError('Failed to start one or more queue consumers', {
|
|
@@ -161,20 +162,21 @@ export class QueueConsumerPlugin extends Plugin {
|
|
|
161
162
|
stop: () => consumer.stop()
|
|
162
163
|
}));
|
|
163
164
|
|
|
164
|
-
const { errors } = await
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
.process(async (task: StopTask) => {
|
|
165
|
+
const { errors } = await TasksPool.map(
|
|
166
|
+
stopTasks,
|
|
167
|
+
async (task: StopTask) => {
|
|
168
168
|
await task.stop();
|
|
169
169
|
return task.consumer;
|
|
170
|
-
}
|
|
170
|
+
},
|
|
171
|
+
{ concurrency: this.stopConcurrency }
|
|
172
|
+
);
|
|
171
173
|
|
|
172
174
|
if (errors.length > 0) {
|
|
173
|
-
errors.forEach((
|
|
174
|
-
const reason =
|
|
175
|
+
errors.forEach((errorInfo) => {
|
|
176
|
+
const reason = errorInfo.error;
|
|
175
177
|
this.logger.warn(
|
|
176
|
-
{ error:
|
|
177
|
-
`Failed to stop consumer: ${
|
|
178
|
+
{ error: reason?.message || reason },
|
|
179
|
+
`Failed to stop consumer: ${reason?.message || reason}`
|
|
178
180
|
);
|
|
179
181
|
});
|
|
180
182
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - Iterates over enabled targets
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { TasksPool } from '../../../tasks/tasks-pool.class.js';
|
|
11
11
|
import { getCronManager } from '../../../concerns/cron-manager.js';
|
|
12
12
|
import type { TargetManager, TargetRecord } from './target-manager.js';
|
|
13
13
|
|
|
@@ -149,9 +149,9 @@ export class SchedulerManager {
|
|
|
149
149
|
targets: activeTargets.map(t => t.id)
|
|
150
150
|
});
|
|
151
151
|
|
|
152
|
-
await
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
await TasksPool.map(
|
|
153
|
+
activeTargets,
|
|
154
|
+
async (targetEntry) => {
|
|
155
155
|
try {
|
|
156
156
|
const report = await this.plugin.runDiagnostics(targetEntry.target, {
|
|
157
157
|
behavior: targetEntry.behavior,
|
|
@@ -177,7 +177,9 @@ export class SchedulerManager {
|
|
|
177
177
|
error
|
|
178
178
|
});
|
|
179
179
|
}
|
|
180
|
-
}
|
|
180
|
+
},
|
|
181
|
+
{ concurrency: this.plugin.config.concurrency || 1 }
|
|
182
|
+
);
|
|
181
183
|
|
|
182
184
|
this.plugin.emit('recon:sweep-completed', {
|
|
183
185
|
reason,
|