mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-11-08 22:02:12 +00:00
chore(ai): document src/lock-ops.mjs
- Added JSDoc to exported functions and classes in src/lock-ops.mjs - Added inline comments to complex logic areas - Created docs/lock-ops-overview.md - Created task log for daily docs-code-investigator agent Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
5590933c5c
commit
8455c99dfc
4 files changed
+182
No files matched your search
@@ -0,0 +1,15 @@
|
||||
# Context: Documentation of src/lock-ops.mjs
|
||||
|
||||
## Selection
|
||||
Selected `src/lock-ops.mjs` for documentation.
|
||||
|
||||
## Reason
|
||||
- **Size & Complexity**: This file is large (> 400 lines) and contains complex logic for relay health management, query execution with fallbacks, and lock publishing with retries.
|
||||
- **Critical Path**: It implements the core locking mechanism for the Torch agent system, ensuring mutual exclusion across distributed agents.
|
||||
- **Lack of Docs**: While some comments exist, public exports lack comprehensive JSDoc, and there is no high-level overview of the interaction between `RelayHealthManager` and `LockPublisher`.
|
||||
|
||||
## Plan
|
||||
1. **Analyze**: Review `src/lock-ops.mjs` to understand the flow of `queryLocks` and `publishLock`.
|
||||
2. **Document**: Add JSDoc to all exported functions and classes. Add inline comments to `RelayHealthManager.rankRelays` and `LockPublisher.executePublishCycle` to explain the logic.
|
||||
3. **Overview**: Create `docs/lock-ops-overview.md` to describe the architecture, sequence of operations, and failure handling strategies.
|
||||
4. **Verify**: Run lint and tests.
|
||||
@@ -23,6 +23,13 @@ function nowUnix() {
|
||||
return Math.floor(Date.now() / 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a raw Nostr event into a structured lock object.
|
||||
* Extracts metadata from tags (d-tag, expiration) and parses the JSON content.
|
||||
*
|
||||
* @param {Object} event - The raw Nostr event object.
|
||||
* @returns {Object} A structured lock object containing event metadata and parsed content.
|
||||
*/
|
||||
export function parseLockEvent(event) {
|
||||
const dTag = event.tags.find((t) => t[0] === 'd')?.[1] ?? '';
|
||||
const expTag = event.tags.find((t) => t[0] === 'expiration')?.[1];
|
||||
@@ -90,6 +97,13 @@ const PUBLISH_FAILURE_CATEGORIES = {
|
||||
NON_RETRYABLE: 'relay_publish_non_retryable',
|
||||
};
|
||||
|
||||
/**
|
||||
* Classifies a raw error message into a standardized publication error code.
|
||||
* Used to determine if a failure is transient (retryable) or permanent.
|
||||
*
|
||||
* @param {string|Error} message - The error message or object to classify.
|
||||
* @returns {string} One of the PUBLISH_ERROR_CODES constants.
|
||||
*/
|
||||
function classifyPublishError(message) {
|
||||
const normalized = String(message || '').toLowerCase();
|
||||
if (normalized.includes('publish timed out after') || normalized.includes('publish timeout')) {
|
||||
@@ -201,6 +215,11 @@ function computeRelayScore(summary) {
|
||||
return (summary.successRate * 1.4) - (summary.timeoutRate * 0.9) - latencyPenalty - quarantinePenalty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages health metrics, scoring, and prioritization for Nostr relays.
|
||||
* Tracks success rates, timeouts, and latency to optimize relay selection.
|
||||
* Implements a quarantine mechanism for failing relays.
|
||||
*/
|
||||
export class RelayHealthManager {
|
||||
constructor() {
|
||||
this.metricsByRelay = new Map();
|
||||
@@ -227,11 +246,21 @@ export class RelayHealthManager {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ranks a list of relays based on their health scores.
|
||||
* Utilizes a cached sort order to avoid re-sorting on every call if metrics haven't changed.
|
||||
*
|
||||
* @param {string[]} relays - List of relay URLs to rank.
|
||||
* @param {Object} config - Health configuration.
|
||||
* @param {number} [nowMs] - Current timestamp.
|
||||
* @returns {Array} List of ranked relay entries with scores and summaries.
|
||||
*/
|
||||
rankRelays(relays, config, nowMs = Date.now()) {
|
||||
for (const relay of relays) {
|
||||
this.ensureMetrics(relay, config);
|
||||
}
|
||||
|
||||
// Check if the cached sort order is still valid
|
||||
if (
|
||||
!this._sortedCache
|
||||
|| this._sortedCache.version !== this._metricsVersion
|
||||
@@ -240,6 +269,7 @@ export class RelayHealthManager {
|
||||
let minQuarantineUntil = Infinity;
|
||||
const entries = [];
|
||||
|
||||
// Re-evaluate scores for all tracked relays
|
||||
for (const metrics of this.metricsByRelay.values()) {
|
||||
const summary = summarizeRelayMetrics(metrics, nowMs);
|
||||
const score = computeRelayScore(summary);
|
||||
@@ -254,6 +284,7 @@ export class RelayHealthManager {
|
||||
});
|
||||
}
|
||||
|
||||
// Sort by score (desc), then quarantine status, then latency (asc), then name
|
||||
entries.sort((a, b) => {
|
||||
if (b.score !== a.score) return b.score - a.score;
|
||||
if (a.summary.quarantined !== b.summary.quarantined) return a.summary.quarantined ? 1 : -1;
|
||||
@@ -290,6 +321,15 @@ export class RelayHealthManager {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a subset of relays for immediate use, prioritizing healthy ones.
|
||||
* May include quarantined relays if the number of healthy relays is below `minActiveRelayPool`.
|
||||
*
|
||||
* @param {string[]} relays - Candidate relays.
|
||||
* @param {Object} config - Health configuration.
|
||||
* @param {number} [nowMs] - Current timestamp.
|
||||
* @returns {Object} An object containing `prioritized` (list of selected relay URLs) and `ranked` (full ranking details).
|
||||
*/
|
||||
prioritizeRelays(relays, config, nowMs = Date.now()) {
|
||||
const minActive = Math.max(1, Math.min(config.minActiveRelayPool, relays.length || 1));
|
||||
const ranked = this.rankRelays([...new Set(relays)], config, nowMs);
|
||||
@@ -310,6 +350,16 @@ export class RelayHealthManager {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates health metrics for a relay based on the outcome of an operation.
|
||||
*
|
||||
* @param {string} relay - The relay URL.
|
||||
* @param {boolean} success - Whether the operation succeeded.
|
||||
* @param {string} errorMessage - Error message if failed.
|
||||
* @param {number} latencyMs - Duration of the operation.
|
||||
* @param {Object} config - Health configuration.
|
||||
* @param {number} [nowMs] - Current timestamp.
|
||||
*/
|
||||
recordOutcome(relay, success, errorMessage, latencyMs, config, nowMs = Date.now()) {
|
||||
const metrics = this.ensureMetrics(relay, config);
|
||||
const timedOut = String(errorMessage || '').toLowerCase().includes('timeout')
|
||||
@@ -383,6 +433,18 @@ function mergeRelayList(primaryRelays, fallbackRelays) {
|
||||
return [...new Set([...primaryRelays, ...fallbackRelays])];
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries relays for active lock events matching the criteria.
|
||||
* Uses a tiered approach: tries primary relays first, then falls back to others if needed.
|
||||
*
|
||||
* @param {string[]} relays - List of primary relay URLs.
|
||||
* @param {string} cadence - The cadence (e.g., 'daily', 'weekly').
|
||||
* @param {string} dateStr - The date string (e.g., '2023-10-27').
|
||||
* @param {string} namespace - The lock namespace (e.g., 'torch').
|
||||
* @param {Object} [deps] - Dependencies and configuration overrides.
|
||||
* @returns {Promise<Array>} A promise resolving to an array of active lock objects.
|
||||
* @throws {Error} If the query fails on all attempted relays (primary + fallback).
|
||||
*/
|
||||
export async function queryLocks(relays, cadence, dateStr, namespace, deps = {}) {
|
||||
const {
|
||||
poolFactory = () => new SimplePool(),
|
||||
@@ -488,7 +550,22 @@ async function publishToRelays(pool, relays, event, publishTimeoutMs, phase) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Orchestrates the publication of a lock event to multiple relays.
|
||||
* Handles retries, fallback relays, and health-based relay prioritization.
|
||||
*
|
||||
* Cycle:
|
||||
* 1. Attempt to publish to primary relays.
|
||||
* 2. If quorum not met, attempt fallback relays.
|
||||
* 3. If still failing, retry with backoff (if errors are transient).
|
||||
* 4. Report final success or failure.
|
||||
*/
|
||||
export class LockPublisher {
|
||||
/**
|
||||
* @param {string[]} relays - Target primary relays.
|
||||
* @param {Object} event - The lock event to publish.
|
||||
* @param {Object} [deps] - Dependencies and configuration.
|
||||
*/
|
||||
constructor(relays, event, deps = {}) {
|
||||
this.relays = relays;
|
||||
this.event = event;
|
||||
@@ -512,6 +589,12 @@ export class LockPublisher {
|
||||
this.attemptId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the publication process with retries.
|
||||
*
|
||||
* @returns {Promise<Object>} The published event if successful.
|
||||
* @throws {Error} If publication quorum is not met after all attempts.
|
||||
*/
|
||||
async publish() {
|
||||
const {
|
||||
poolFactory = () => new SimplePool(),
|
||||
@@ -560,6 +643,7 @@ export class LockPublisher {
|
||||
const overallStartedAt = Date.now();
|
||||
let terminalFailureCategory = PUBLISH_FAILURE_CATEGORIES.NON_RETRYABLE;
|
||||
|
||||
// Retry loop: attempts publication until success or max attempts reached
|
||||
for (let attemptNumber = 1; attemptNumber <= this.maxAttempts; attemptNumber += 1) {
|
||||
const startedAtMs = Date.now();
|
||||
lastAttemptState = await this.executePublishCycle();
|
||||
@@ -731,6 +815,15 @@ export class LockPublisher {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* High-level function to publish a lock event.
|
||||
* Initializes a LockPublisher and triggers the publication process.
|
||||
*
|
||||
* @param {string[]} relays - Target relays.
|
||||
* @param {Object} event - Lock event to publish.
|
||||
* @param {Object} [deps] - Dependencies.
|
||||
* @returns {Promise<Object>} The published event.
|
||||
*/
|
||||
export async function publishLock(relays, event, deps = {}) {
|
||||
const {
|
||||
getPublishTimeoutMsFn = getPublishTimeoutMs,
|
||||
|
||||
Reference in new issue
Block a user