tangled
alpha
login
or
join now
nekomimi.pet
/
wisp.place-monorepo
87
fork
atom
Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol.
wisp.place
87
fork
atom
overview
issues
9
pulls
pipelines
lowk i forgot what i even did
nekomimi.pet
3 weeks ago
e2004b6b
315552b7
1/2
deploy-wisp.yml
success
1m 12s
test.yml
failed
31s
+53
-42
2 changed files
expand all
collapse all
unified
split
apps
firehose-service
src
lib
cache-writer.ts
packages
@wispplace
tiered-storage
src
tiers
S3StorageTier.ts
+51
-40
apps/firehose-service/src/lib/cache-writer.ts
reviewed
···
734
734
throw new SiteBlobBackoffError(did, rkey, incrementalBackoffUntil, downloadFailures.length);
735
735
}
736
736
737
737
-
// Recovery path: wipe site prefix and perform full rebuild if incremental had failures
737
737
+
// Recovery path: retry only the files/keys that failed, keeping successful downloads intact
738
738
if (downloadFailures.length > 0 || deleteFailures.length > 0) {
739
739
-
logger.warn(`Incremental sync failed for ${did}/${rkey}; falling back to full rebuild`, {
739
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
746
-
const prefix = `${did}/${rkey}/`;
747
747
-
const existingKeys = await listFiles(prefix);
748
748
-
const wipeFailures = await deleteKeys(existingKeys);
749
749
-
if (wipeFailures.length > 0) {
750
750
-
logger.error(`Failed to wipe site prefix before full rebuild for ${did}/${rkey}`, undefined, {
751
751
-
did,
752
752
-
rkey,
753
753
-
wipeFailures: wipeFailures.length,
754
754
-
sampleFailures: wipeFailures.slice(0, 5).map((f) => ({
755
755
-
key: f.key,
756
756
-
error: f.error instanceof Error ? f.error.message : String(f.error),
757
757
-
})),
758
758
-
});
759
759
-
throw new Error(`Failed to wipe site prefix for ${did}/${rkey}`);
760
760
-
}
746
746
+
if (downloadFailures.length > 0) {
747
747
+
const failedPaths = new Set(downloadFailures.map((f) => f.path));
748
748
+
const failedFiles = filesToDownload.filter((f) => failedPaths.has(f.path));
749
749
+
const retryDownloadFailures = await downloadFiles(failedFiles);
761
750
762
762
-
const fullDownloadFailures = await downloadFiles(newFiles);
763
763
-
if (fullDownloadFailures.length > 0) {
764
764
-
const fullBackoffUntil = fullDownloadFailures.reduce<number | null>((maxUntil, failure) => {
765
765
-
const until = getBlobBackoffUntil(failure.error);
766
766
-
if (!until) return maxUntil;
767
767
-
if (!maxUntil) return until;
768
768
-
return Math.max(maxUntil, until);
769
769
-
}, null);
770
770
-
const allFullDownloadsBackoffed =
771
771
-
fullDownloadFailures.length > 0 &&
772
772
-
fullDownloadFailures.every((failure) => getBlobBackoffUntil(failure.error) !== null);
751
751
+
if (retryDownloadFailures.length > 0) {
752
752
+
const retryBackoffUntil = retryDownloadFailures.reduce<number | null>((maxUntil, failure) => {
753
753
+
const until = getBlobBackoffUntil(failure.error);
754
754
+
if (!until) return maxUntil;
755
755
+
if (!maxUntil) return until;
756
756
+
return Math.max(maxUntil, until);
757
757
+
}, null);
758
758
+
const allRetryBackoffed =
759
759
+
retryDownloadFailures.every((failure) => getBlobBackoffUntil(failure.error) !== null);
760
760
+
761
761
+
logger.error(`Retry of failed downloads failed for ${did}/${rkey}`, undefined, {
762
762
+
did,
763
763
+
rkey,
764
764
+
retryDownloadFailures: retryDownloadFailures.length,
765
765
+
sampleFailures: retryDownloadFailures.slice(0, 5).map((f) => ({
766
766
+
path: f.path,
767
767
+
error: f.error instanceof Error ? f.error.message : String(f.error),
768
768
+
})),
769
769
+
});
770
770
+
771
771
+
if (!options?.skipInvalidation) {
772
772
+
await publishCacheInvalidation(did, rkey, 'update').catch(() => undefined);
773
773
+
}
773
774
774
774
-
logger.error(`Full rebuild failed for ${did}/${rkey}`, undefined, {
775
775
-
did,
776
776
-
rkey,
777
777
-
fullDownloadFailures: fullDownloadFailures.length,
778
778
-
sampleFailures: fullDownloadFailures.slice(0, 5).map((f) => ({
779
779
-
path: f.path,
780
780
-
error: f.error instanceof Error ? f.error.message : String(f.error),
781
781
-
})),
782
782
-
});
775
775
+
if (allRetryBackoffed && retryBackoffUntil) {
776
776
+
throw new SiteBlobBackoffError(did, rkey, retryBackoffUntil, retryDownloadFailures.length);
777
777
+
}
783
778
784
784
-
if (allFullDownloadsBackoffed && fullBackoffUntil) {
785
785
-
throw new SiteBlobBackoffError(did, rkey, fullBackoffUntil, fullDownloadFailures.length);
779
779
+
throw new Error(`Failed to download files for ${did}/${rkey}`);
786
780
}
781
781
+
}
787
782
788
788
-
throw new Error(`Full rebuild failed for ${did}/${rkey}`);
783
783
+
if (deleteFailures.length > 0) {
784
784
+
const retryDeleteFailures = await deleteKeys(deleteFailures.map((f) => f.key));
785
785
+
if (retryDeleteFailures.length > 0) {
786
786
+
logger.error(`Retry of failed deletes failed for ${did}/${rkey}`, undefined, {
787
787
+
did,
788
788
+
rkey,
789
789
+
retryDeleteFailures: retryDeleteFailures.length,
790
790
+
sampleFailures: retryDeleteFailures.slice(0, 5).map((f) => ({
791
791
+
key: f.key,
792
792
+
error: f.error instanceof Error ? f.error.message : String(f.error),
793
793
+
})),
794
794
+
});
795
795
+
if (!options?.skipInvalidation) {
796
796
+
await publishCacheInvalidation(did, rkey, 'update').catch(() => undefined);
797
797
+
}
798
798
+
throw new Error(`Failed to delete files for ${did}/${rkey}`);
799
799
+
}
789
800
}
790
801
}
791
802
+2
-2
packages/@wispplace/tiered-storage/src/tiers/S3StorageTier.ts
reviewed
···
622
622
*/
623
623
private metadataToS3(metadata: StorageMetadata): Record<string, string> {
624
624
return {
625
625
-
key: metadata.key,
625
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
644
-
key: s3Metadata.key ?? '',
644
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()),