Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol. wisp.place

remove bun from observability

nekomimi.pet 9ddca0e8 436281db

verified
Changed files
+75 -68
apps
hosting-service
src
packages
@wisp
observability
+65 -57
apps/hosting-service/src/lib/backfill.ts
··· 60 60 console.log(`⚙️ Limited to ${maxSites} sites for backfill`); 61 61 } 62 62 63 - // Process sites in batches 64 - const batches: typeof sites[] = []; 65 - for (let i = 0; i < sites.length; i += concurrency) { 66 - batches.push(sites.slice(i, i + concurrency)); 67 - } 68 - 63 + // Process sites with sliding window concurrency pool 64 + const executing = new Set<Promise<void>>(); 69 65 let processed = 0; 70 - for (const batch of batches) { 71 - await Promise.all( 72 - batch.map(async (site) => { 73 - try { 74 - // Check if already cached 75 - if (skipExisting && isCached(site.did, site.rkey)) { 76 - stats.skipped++; 77 - processed++; 78 - logger.debug(`Skipping already cached site`, { did: site.did, rkey: site.rkey }); 79 - console.log(`⏭️ [${processed}/${sites.length}] Skipped (cached): ${site.display_name || site.rkey}`); 80 - return; 81 - } 82 66 83 - // Fetch site record 84 - const siteData = await fetchSiteRecord(site.did, site.rkey); 85 - if (!siteData) { 86 - stats.failed++; 87 - processed++; 88 - logger.error('Site record not found during backfill', null, { did: site.did, rkey: site.rkey }); 89 - console.log(`❌ [${processed}/${sites.length}] Failed (not found): ${site.display_name || site.rkey}`); 90 - return; 91 - } 92 - 93 - // Get PDS endpoint 94 - const pdsEndpoint = await getPdsForDid(site.did); 95 - if (!pdsEndpoint) { 96 - stats.failed++; 97 - processed++; 98 - logger.error('PDS not found during backfill', null, { did: site.did }); 99 - console.log(`❌ [${processed}/${sites.length}] Failed (no PDS): ${site.display_name || site.rkey}`); 100 - return; 101 - } 67 + for (const site of sites) { 68 + // Create task for this site 69 + const processSite = async () => { 70 + try { 71 + // Check if already cached 72 + if (skipExisting && isCached(site.did, site.rkey)) { 73 + stats.skipped++; 74 + processed++; 75 + logger.debug(`Skipping already cached site`, { did: site.did, rkey: site.rkey }); 76 + console.log(`⏭️ [${processed}/${sites.length}] Skipped (cached): ${site.display_name || site.rkey}`); 77 + return; 78 + } 102 79 103 - // Mark site as being cached to prevent serving stale content during update 104 - markSiteAsBeingCached(site.did, site.rkey); 80 + // Fetch site record 81 + const siteData = await fetchSiteRecord(site.did, site.rkey); 82 + if (!siteData) { 83 + stats.failed++; 84 + processed++; 85 + logger.error('Site record not found during backfill', null, { did: site.did, rkey: site.rkey }); 86 + console.log(`❌ [${processed}/${sites.length}] Failed (not found): ${site.display_name || site.rkey}`); 87 + return; 88 + } 105 89 106 - try { 107 - // Download and cache site 108 - await downloadAndCacheSite(site.did, site.rkey, siteData.record, pdsEndpoint, siteData.cid); 109 - // Clear redirect rules cache since the site was updated 110 - clearRedirectRulesCache(site.did, site.rkey); 111 - stats.cached++; 112 - processed++; 113 - logger.info('Successfully cached site during backfill', { did: site.did, rkey: site.rkey }); 114 - console.log(`✅ [${processed}/${sites.length}] Cached: ${site.display_name || site.rkey}`); 115 - } finally { 116 - // Always unmark, even if caching fails 117 - unmarkSiteAsBeingCached(site.did, site.rkey); 118 - } 119 - } catch (err) { 90 + // Get PDS endpoint 91 + const pdsEndpoint = await getPdsForDid(site.did); 92 + if (!pdsEndpoint) { 120 93 stats.failed++; 121 94 processed++; 122 - logger.error('Failed to cache site during backfill', err, { did: site.did, rkey: site.rkey }); 123 - console.log(`❌ [${processed}/${sites.length}] Failed: ${site.display_name || site.rkey}`); 95 + logger.error('PDS not found during backfill', null, { did: site.did }); 96 + console.log(`❌ [${processed}/${sites.length}] Failed (no PDS): ${site.display_name || site.rkey}`); 97 + return; 98 + } 99 + 100 + // Mark site as being cached to prevent serving stale content during update 101 + markSiteAsBeingCached(site.did, site.rkey); 102 + 103 + try { 104 + // Download and cache site 105 + await downloadAndCacheSite(site.did, site.rkey, siteData.record, pdsEndpoint, siteData.cid); 106 + // Clear redirect rules cache since the site was updated 107 + clearRedirectRulesCache(site.did, site.rkey); 108 + stats.cached++; 109 + processed++; 110 + logger.info('Successfully cached site during backfill', { did: site.did, rkey: site.rkey }); 111 + console.log(`✅ [${processed}/${sites.length}] Cached: ${site.display_name || site.rkey}`); 112 + } finally { 113 + // Always unmark, even if caching fails 114 + unmarkSiteAsBeingCached(site.did, site.rkey); 124 115 } 125 - }) 126 - ); 116 + } catch (err) { 117 + stats.failed++; 118 + processed++; 119 + logger.error('Failed to cache site during backfill', err, { did: site.did, rkey: site.rkey }); 120 + console.log(`❌ [${processed}/${sites.length}] Failed: ${site.display_name || site.rkey}`); 121 + } 122 + }; 123 + 124 + // Add to executing pool and remove when done 125 + const promise = processSite().finally(() => executing.delete(promise)); 126 + executing.add(promise); 127 + 128 + // When pool is full, wait for at least one to complete 129 + if (executing.size >= concurrency) { 130 + await Promise.race(executing); 131 + } 127 132 } 133 + 134 + // Wait for all remaining tasks to complete 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 - const env = typeof Bun !== 'undefined' ? Bun.env.NODE_ENV : process.env.NODE_ENV; 172 - if (env !== 'production') { 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 - this.config.lokiUrl = process.env.GRAFANA_LOKI_URL || Bun?.env?.GRAFANA_LOKI_URL 64 + this.config.lokiUrl = process.env.GRAFANA_LOKI_URL 65 65 } 66 66 67 67 if (!this.config.prometheusUrl) { 68 - this.config.prometheusUrl = process.env.GRAFANA_PROMETHEUS_URL || Bun?.env?.GRAFANA_PROMETHEUS_URL 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 - const token = process.env.GRAFANA_LOKI_TOKEN || Bun?.env?.GRAFANA_LOKI_TOKEN 74 - const username = process.env.GRAFANA_LOKI_USERNAME || Bun?.env?.GRAFANA_LOKI_USERNAME 75 - const password = process.env.GRAFANA_LOKI_PASSWORD || Bun?.env?.GRAFANA_LOKI_PASSWORD 73 + const token = process.env.GRAFANA_LOKI_TOKEN 74 + const username = process.env.GRAFANA_LOKI_USERNAME 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 - const token = process.env.GRAFANA_PROMETHEUS_TOKEN || Bun?.env?.GRAFANA_PROMETHEUS_TOKEN 87 - const username = process.env.GRAFANA_PROMETHEUS_USERNAME || Bun?.env?.GRAFANA_PROMETHEUS_USERNAME 88 - const password = process.env.GRAFANA_PROMETHEUS_PASSWORD || Bun?.env?.GRAFANA_PROMETHEUS_PASSWORD 86 + const token = process.env.GRAFANA_PROMETHEUS_TOKEN 87 + const username = process.env.GRAFANA_PROMETHEUS_USERNAME 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 - private flushTimer?: Timer | NodeJS.Timer 123 + private flushTimer?: NodeJS.Timeout 124 124 private config: GrafanaConfig = {} 125 125 126 126 initialize(config: GrafanaConfig) {