tangled
alpha
login
or
join now
notjack.space
/
wisp.place-monorepo
forked from
nekomimi.pet/wisp.place-monorepo
0
fork
atom
Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol.
0
fork
atom
overview
issues
pulls
pipelines
remove bun from observability
nekomimi.pet
3 months ago
9ddca0e8
436281db
verified
This commit was signed with the committer's
known signature
.
nekomimi.pet
SSH Key Fingerprint:
SHA256:knUvGhH8rbrdqbzO9WXAIaTK0LrdYw2UC0qWB43Ic0Q=
+75
-68
3 changed files
expand all
collapse all
unified
split
apps
hosting-service
src
lib
backfill.ts
packages
@wisp
observability
src
core.ts
exporters.ts
+65
-57
apps/hosting-service/src/lib/backfill.ts
···
60
60
console.log(`⚙️ Limited to ${maxSites} sites for backfill`);
61
61
}
62
62
63
63
-
// Process sites in batches
64
64
-
const batches: typeof sites[] = [];
65
65
-
for (let i = 0; i < sites.length; i += concurrency) {
66
66
-
batches.push(sites.slice(i, i + concurrency));
67
67
-
}
68
68
-
63
63
+
// Process sites with sliding window concurrency pool
64
64
+
const executing = new Set<Promise<void>>();
69
65
let processed = 0;
70
70
-
for (const batch of batches) {
71
71
-
await Promise.all(
72
72
-
batch.map(async (site) => {
73
73
-
try {
74
74
-
// Check if already cached
75
75
-
if (skipExisting && isCached(site.did, site.rkey)) {
76
76
-
stats.skipped++;
77
77
-
processed++;
78
78
-
logger.debug(`Skipping already cached site`, { did: site.did, rkey: site.rkey });
79
79
-
console.log(`⏭️ [${processed}/${sites.length}] Skipped (cached): ${site.display_name || site.rkey}`);
80
80
-
return;
81
81
-
}
82
66
83
83
-
// Fetch site record
84
84
-
const siteData = await fetchSiteRecord(site.did, site.rkey);
85
85
-
if (!siteData) {
86
86
-
stats.failed++;
87
87
-
processed++;
88
88
-
logger.error('Site record not found during backfill', null, { did: site.did, rkey: site.rkey });
89
89
-
console.log(`❌ [${processed}/${sites.length}] Failed (not found): ${site.display_name || site.rkey}`);
90
90
-
return;
91
91
-
}
92
92
-
93
93
-
// Get PDS endpoint
94
94
-
const pdsEndpoint = await getPdsForDid(site.did);
95
95
-
if (!pdsEndpoint) {
96
96
-
stats.failed++;
97
97
-
processed++;
98
98
-
logger.error('PDS not found during backfill', null, { did: site.did });
99
99
-
console.log(`❌ [${processed}/${sites.length}] Failed (no PDS): ${site.display_name || site.rkey}`);
100
100
-
return;
101
101
-
}
67
67
+
for (const site of sites) {
68
68
+
// Create task for this site
69
69
+
const processSite = async () => {
70
70
+
try {
71
71
+
// Check if already cached
72
72
+
if (skipExisting && isCached(site.did, site.rkey)) {
73
73
+
stats.skipped++;
74
74
+
processed++;
75
75
+
logger.debug(`Skipping already cached site`, { did: site.did, rkey: site.rkey });
76
76
+
console.log(`⏭️ [${processed}/${sites.length}] Skipped (cached): ${site.display_name || site.rkey}`);
77
77
+
return;
78
78
+
}
102
79
103
103
-
// Mark site as being cached to prevent serving stale content during update
104
104
-
markSiteAsBeingCached(site.did, site.rkey);
80
80
+
// Fetch site record
81
81
+
const siteData = await fetchSiteRecord(site.did, site.rkey);
82
82
+
if (!siteData) {
83
83
+
stats.failed++;
84
84
+
processed++;
85
85
+
logger.error('Site record not found during backfill', null, { did: site.did, rkey: site.rkey });
86
86
+
console.log(`❌ [${processed}/${sites.length}] Failed (not found): ${site.display_name || site.rkey}`);
87
87
+
return;
88
88
+
}
105
89
106
106
-
try {
107
107
-
// Download and cache site
108
108
-
await downloadAndCacheSite(site.did, site.rkey, siteData.record, pdsEndpoint, siteData.cid);
109
109
-
// Clear redirect rules cache since the site was updated
110
110
-
clearRedirectRulesCache(site.did, site.rkey);
111
111
-
stats.cached++;
112
112
-
processed++;
113
113
-
logger.info('Successfully cached site during backfill', { did: site.did, rkey: site.rkey });
114
114
-
console.log(`✅ [${processed}/${sites.length}] Cached: ${site.display_name || site.rkey}`);
115
115
-
} finally {
116
116
-
// Always unmark, even if caching fails
117
117
-
unmarkSiteAsBeingCached(site.did, site.rkey);
118
118
-
}
119
119
-
} catch (err) {
90
90
+
// Get PDS endpoint
91
91
+
const pdsEndpoint = await getPdsForDid(site.did);
92
92
+
if (!pdsEndpoint) {
120
93
stats.failed++;
121
94
processed++;
122
122
-
logger.error('Failed to cache site during backfill', err, { did: site.did, rkey: site.rkey });
123
123
-
console.log(`❌ [${processed}/${sites.length}] Failed: ${site.display_name || site.rkey}`);
95
95
+
logger.error('PDS not found during backfill', null, { did: site.did });
96
96
+
console.log(`❌ [${processed}/${sites.length}] Failed (no PDS): ${site.display_name || site.rkey}`);
97
97
+
return;
98
98
+
}
99
99
+
100
100
+
// Mark site as being cached to prevent serving stale content during update
101
101
+
markSiteAsBeingCached(site.did, site.rkey);
102
102
+
103
103
+
try {
104
104
+
// Download and cache site
105
105
+
await downloadAndCacheSite(site.did, site.rkey, siteData.record, pdsEndpoint, siteData.cid);
106
106
+
// Clear redirect rules cache since the site was updated
107
107
+
clearRedirectRulesCache(site.did, site.rkey);
108
108
+
stats.cached++;
109
109
+
processed++;
110
110
+
logger.info('Successfully cached site during backfill', { did: site.did, rkey: site.rkey });
111
111
+
console.log(`✅ [${processed}/${sites.length}] Cached: ${site.display_name || site.rkey}`);
112
112
+
} finally {
113
113
+
// Always unmark, even if caching fails
114
114
+
unmarkSiteAsBeingCached(site.did, site.rkey);
124
115
}
125
125
-
})
126
126
-
);
116
116
+
} catch (err) {
117
117
+
stats.failed++;
118
118
+
processed++;
119
119
+
logger.error('Failed to cache site during backfill', err, { did: site.did, rkey: site.rkey });
120
120
+
console.log(`❌ [${processed}/${sites.length}] Failed: ${site.display_name || site.rkey}`);
121
121
+
}
122
122
+
};
123
123
+
124
124
+
// Add to executing pool and remove when done
125
125
+
const promise = processSite().finally(() => executing.delete(promise));
126
126
+
executing.add(promise);
127
127
+
128
128
+
// When pool is full, wait for at least one to complete
129
129
+
if (executing.size >= concurrency) {
130
130
+
await Promise.race(executing);
131
131
+
}
127
132
}
133
133
+
134
134
+
// Wait for all remaining tasks to complete
135
135
+
await Promise.all(executing);
128
136
129
137
stats.duration = Date.now() - startTime;
130
138
+1
-2
packages/@wisp/observability/src/core.ts
···
168
168
},
169
169
170
170
debug(message: string, service: string, context?: Record<string, any>, traceId?: string) {
171
171
-
const env = typeof Bun !== 'undefined' ? Bun.env.NODE_ENV : process.env.NODE_ENV;
172
172
-
if (env !== 'production') {
171
171
+
if (process.env.NODE_ENV !== 'production') {
173
172
this.log('debug', message, service, context, traceId)
174
173
}
175
174
},
+9
-9
packages/@wisp/observability/src/exporters.ts
···
61
61
62
62
// Load from environment variables if not provided
63
63
if (!this.config.lokiUrl) {
64
64
-
this.config.lokiUrl = process.env.GRAFANA_LOKI_URL || Bun?.env?.GRAFANA_LOKI_URL
64
64
+
this.config.lokiUrl = process.env.GRAFANA_LOKI_URL
65
65
}
66
66
67
67
if (!this.config.prometheusUrl) {
68
68
-
this.config.prometheusUrl = process.env.GRAFANA_PROMETHEUS_URL || Bun?.env?.GRAFANA_PROMETHEUS_URL
68
68
+
this.config.prometheusUrl = process.env.GRAFANA_PROMETHEUS_URL
69
69
}
70
70
71
71
// Load Loki authentication from environment
72
72
if (!this.config.lokiAuth?.bearerToken && !this.config.lokiAuth?.username) {
73
73
-
const token = process.env.GRAFANA_LOKI_TOKEN || Bun?.env?.GRAFANA_LOKI_TOKEN
74
74
-
const username = process.env.GRAFANA_LOKI_USERNAME || Bun?.env?.GRAFANA_LOKI_USERNAME
75
75
-
const password = process.env.GRAFANA_LOKI_PASSWORD || Bun?.env?.GRAFANA_LOKI_PASSWORD
73
73
+
const token = process.env.GRAFANA_LOKI_TOKEN
74
74
+
const username = process.env.GRAFANA_LOKI_USERNAME
75
75
+
const password = process.env.GRAFANA_LOKI_PASSWORD
76
76
77
77
if (token) {
78
78
this.config.lokiAuth = { ...this.config.lokiAuth, bearerToken: token }
···
83
83
84
84
// Load Prometheus authentication from environment
85
85
if (!this.config.prometheusAuth?.bearerToken && !this.config.prometheusAuth?.username) {
86
86
-
const token = process.env.GRAFANA_PROMETHEUS_TOKEN || Bun?.env?.GRAFANA_PROMETHEUS_TOKEN
87
87
-
const username = process.env.GRAFANA_PROMETHEUS_USERNAME || Bun?.env?.GRAFANA_PROMETHEUS_USERNAME
88
88
-
const password = process.env.GRAFANA_PROMETHEUS_PASSWORD || Bun?.env?.GRAFANA_PROMETHEUS_PASSWORD
86
86
+
const token = process.env.GRAFANA_PROMETHEUS_TOKEN
87
87
+
const username = process.env.GRAFANA_PROMETHEUS_USERNAME
88
88
+
const password = process.env.GRAFANA_PROMETHEUS_PASSWORD
89
89
90
90
if (token) {
91
91
this.config.prometheusAuth = { ...this.config.prometheusAuth, bearerToken: token }
···
120
120
class LokiExporter {
121
121
private buffer: LogEntry[] = []
122
122
private errorBuffer: ErrorEntry[] = []
123
123
-
private flushTimer?: Timer | NodeJS.Timer
123
123
+
private flushTimer?: NodeJS.Timeout
124
124
private config: GrafanaConfig = {}
125
125
126
126
initialize(config: GrafanaConfig) {