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

lowk i forgot what i even did

+53 -42
+51 -40
apps/firehose-service/src/lib/cache-writer.ts
··· 734 734 throw new SiteBlobBackoffError(did, rkey, incrementalBackoffUntil, downloadFailures.length); 735 735 } 736 736 737 - // Recovery path: wipe site prefix and perform full rebuild if incremental had failures 737 + // Recovery path: retry only the files/keys that failed, keeping successful downloads intact 738 738 if (downloadFailures.length > 0 || deleteFailures.length > 0) { 739 - logger.warn(`Incremental sync failed for ${did}/${rkey}; falling back to full rebuild`, { 739 + logger.warn(`Incremental sync had failures for ${did}/${rkey}; retrying failed operations`, { 740 740 did, 741 741 rkey, 742 742 downloadFailures: downloadFailures.length, 743 743 deleteFailures: deleteFailures.length, 744 744 }); 745 745 746 - const prefix = `${did}/${rkey}/`; 747 - const existingKeys = await listFiles(prefix); 748 - const wipeFailures = await deleteKeys(existingKeys); 749 - if (wipeFailures.length > 0) { 750 - logger.error(`Failed to wipe site prefix before full rebuild for ${did}/${rkey}`, undefined, { 751 - did, 752 - rkey, 753 - wipeFailures: wipeFailures.length, 754 - sampleFailures: wipeFailures.slice(0, 5).map((f) => ({ 755 - key: f.key, 756 - error: f.error instanceof Error ? f.error.message : String(f.error), 757 - })), 758 - }); 759 - throw new Error(`Failed to wipe site prefix for ${did}/${rkey}`); 760 - } 746 + if (downloadFailures.length > 0) { 747 + const failedPaths = new Set(downloadFailures.map((f) => f.path)); 748 + const failedFiles = filesToDownload.filter((f) => failedPaths.has(f.path)); 749 + const retryDownloadFailures = await downloadFiles(failedFiles); 761 750 762 - const fullDownloadFailures = await downloadFiles(newFiles); 763 - if (fullDownloadFailures.length > 0) { 764 - const fullBackoffUntil = fullDownloadFailures.reduce<number | null>((maxUntil, failure) => { 765 - const until = getBlobBackoffUntil(failure.error); 766 - if (!until) return maxUntil; 767 - if (!maxUntil) return until; 768 - return Math.max(maxUntil, until); 769 - }, null); 770 - const allFullDownloadsBackoffed = 771 - fullDownloadFailures.length > 0 && 772 - fullDownloadFailures.every((failure) => getBlobBackoffUntil(failure.error) !== null); 751 + if (retryDownloadFailures.length > 0) { 752 + const retryBackoffUntil = retryDownloadFailures.reduce<number | null>((maxUntil, failure) => { 753 + const until = getBlobBackoffUntil(failure.error); 754 + if (!until) return maxUntil; 755 + if (!maxUntil) return until; 756 + return Math.max(maxUntil, until); 757 + }, null); 758 + const allRetryBackoffed = 759 + retryDownloadFailures.every((failure) => getBlobBackoffUntil(failure.error) !== null); 760 + 761 + logger.error(`Retry of failed downloads failed for ${did}/${rkey}`, undefined, { 762 + did, 763 + rkey, 764 + retryDownloadFailures: retryDownloadFailures.length, 765 + sampleFailures: retryDownloadFailures.slice(0, 5).map((f) => ({ 766 + path: f.path, 767 + error: f.error instanceof Error ? f.error.message : String(f.error), 768 + })), 769 + }); 770 + 771 + if (!options?.skipInvalidation) { 772 + await publishCacheInvalidation(did, rkey, 'update').catch(() => undefined); 773 + } 773 774 774 - logger.error(`Full rebuild failed for ${did}/${rkey}`, undefined, { 775 - did, 776 - rkey, 777 - fullDownloadFailures: fullDownloadFailures.length, 778 - sampleFailures: fullDownloadFailures.slice(0, 5).map((f) => ({ 779 - path: f.path, 780 - error: f.error instanceof Error ? f.error.message : String(f.error), 781 - })), 782 - }); 775 + if (allRetryBackoffed && retryBackoffUntil) { 776 + throw new SiteBlobBackoffError(did, rkey, retryBackoffUntil, retryDownloadFailures.length); 777 + } 783 778 784 - if (allFullDownloadsBackoffed && fullBackoffUntil) { 785 - throw new SiteBlobBackoffError(did, rkey, fullBackoffUntil, fullDownloadFailures.length); 779 + throw new Error(`Failed to download files for ${did}/${rkey}`); 786 780 } 781 + } 787 782 788 - throw new Error(`Full rebuild failed for ${did}/${rkey}`); 783 + if (deleteFailures.length > 0) { 784 + const retryDeleteFailures = await deleteKeys(deleteFailures.map((f) => f.key)); 785 + if (retryDeleteFailures.length > 0) { 786 + logger.error(`Retry of failed deletes failed for ${did}/${rkey}`, undefined, { 787 + did, 788 + rkey, 789 + retryDeleteFailures: retryDeleteFailures.length, 790 + sampleFailures: retryDeleteFailures.slice(0, 5).map((f) => ({ 791 + key: f.key, 792 + error: f.error instanceof Error ? f.error.message : String(f.error), 793 + })), 794 + }); 795 + if (!options?.skipInvalidation) { 796 + await publishCacheInvalidation(did, rkey, 'update').catch(() => undefined); 797 + } 798 + throw new Error(`Failed to delete files for ${did}/${rkey}`); 799 + } 789 800 } 790 801 } 791 802
+2 -2
packages/@wispplace/tiered-storage/src/tiers/S3StorageTier.ts
··· 622 622 */ 623 623 private metadataToS3(metadata: StorageMetadata): Record<string, string> { 624 624 return { 625 - key: metadata.key, 625 + key: encodeURIComponent(metadata.key), 626 626 size: metadata.size.toString(), 627 627 createdat: metadata.createdAt.toISOString(), 628 628 lastaccessed: metadata.lastAccessed.toISOString(), ··· 641 641 */ 642 642 private s3ToMetadata(s3Metadata: Record<string, string>): StorageMetadata { 643 643 const metadata: StorageMetadata = { 644 - key: s3Metadata.key ?? '', 644 + key: s3Metadata.key ? decodeURIComponent(s3Metadata.key) : '', 645 645 size: parseInt(s3Metadata.size ?? '0', 10), 646 646 createdAt: new Date(s3Metadata.createdat ?? Date.now()), 647 647 lastAccessed: new Date(s3Metadata.lastaccessed ?? Date.now()),