Deduplicate stored upload files and retain adoption bytes until their Root is durable
This commit is contained in:
@@ -79,7 +79,7 @@ export async function openProjectEngine(options: EngineOptions) {
|
||||
const network = createNetworkGate(2);
|
||||
const depot=options.assetDepot??(options.storage.assetDepot?createAssetDepot({scope:options.scope,port:options.storage.assetDepot,current:()=>!closing&&!closed&&(!options.isCurrentIdentity||options.isCurrentIdentity()),digest:sourceDigest}):undefined);
|
||||
if(depot&&(depot.scope.accountId!==options.scope.accountId||depot.scope.workspaceId!==options.scope.workspaceId))throw fault('asset_depot_scope_mismatch');
|
||||
const adoptionTasks=new Map<AbortController,Promise<Result>>(),adoptedFiles=new Map<Id,Blob>();
|
||||
const adoptionTasks=new Map<AbortController,Promise<Result>>(),adoptedFiles=new Map<Id,{readonly bytes:Blob;readonly rootRevision:number}>();
|
||||
let adopting:SharedAsset|undefined;
|
||||
const runClock: Clock = options.clock ?? { now: () => Date.now(), setTimer: (fn, delay) => setTimeout(fn, delay), clearTimer: handle => clearTimeout(handle as ReturnType<typeof setTimeout>) };
|
||||
const displays = new Set<() => void>();
|
||||
@@ -198,7 +198,7 @@ export async function openProjectEngine(options: EngineOptions) {
|
||||
durableRootRevision = revision; pending.delete(revision);
|
||||
for(const id of deleteFiles){durablyPurged.add(id);collectionReservations.delete(id);}
|
||||
for (const file of files) fileWrites.delete(file.id);
|
||||
for(const id of snapshot.runtime.assetBindings?.keys()??[])adoptedFiles.delete(id);
|
||||
for(const [id,entry]of adoptedFiles)if(entry.rootRevision<=snapshot.rootRevision&&depot?.writes.some(write=>write.ref.asset.id===id&&write.bytes===entry.bytes))adoptedFiles.delete(id);
|
||||
if (editor.getSnapshot().runtime.ledger.durableGeneration < snapshot.runtime.ledger.generation) mutate(ledger => ledger.markDurable(snapshot.runtime.ledger.generation));
|
||||
}
|
||||
persistenceFailed = false;
|
||||
@@ -922,9 +922,9 @@ export async function openProjectEngine(options: EngineOptions) {
|
||||
assertWritable(false);signal.throwIfAborted();if(authorized.ref.asset.url!==options.assets!.sourceURL(id))throw fault('asset_authorization_mismatch');
|
||||
if(staged.length||adopting)throw fault('reentrant_dispatch');const runtime=editor.getSnapshot().runtime;
|
||||
if(runtime.resources.has(id)||runtime.orphanFiles?.has(id)||importFiles(runtime.imports).has(id)||fileWrites.has(id)||collectionReservations.has(id))throw fault('resource_identity_conflict',[id]);
|
||||
const previous=adoptedFiles.get(id);adopting=authorized.ref;adoptedFiles.set(id,authorized.bytes);
|
||||
const previous=adoptedFiles.get(id);adopting=authorized.ref;adoptedFiles.set(id,{bytes:authorized.bytes,rootRevision:Number.MAX_SAFE_INTEGER});
|
||||
let committed=false;
|
||||
try{const result=editor.transaction(tx=>callback(freeze({id,src:authorized.ref.asset.url,asset:authorized.ref.asset}),tx),{label:'Add existing image'});committed=result.status==='applied'&&result.operationIds.length>0;return result;}
|
||||
try{const result=editor.transaction(tx=>callback(freeze({id,src:authorized.ref.asset.url,asset:authorized.ref.asset}),tx),{label:'Add existing image'});committed=result.status==='applied'&&result.operationIds.length>0;if(committed)adoptedFiles.set(id,{bytes:authorized.bytes,rootRevision:editor.getSnapshot().rootRevision});return result;}
|
||||
finally{adopting=undefined;if(!committed){if(previous)adoptedFiles.set(id,previous);else adoptedFiles.delete(id);}}
|
||||
})();
|
||||
adoptionTasks.set(controller,task);void task.finally(()=>adoptionTasks.delete(controller)).catch(()=>{});return task;
|
||||
@@ -955,7 +955,7 @@ export async function openProjectEngine(options: EngineOptions) {
|
||||
const binding=editor.getSnapshot().runtime.assetBindings?.get(id);
|
||||
const key=JSON.stringify(binding?['shared-asset',id,binding.authority,binding.digest]:retained?['asset',id,retained.operationId,retained.retirement?.purgedAt??null]:orphan?['orphan',id,orphan.digest]:source?['source',id,source.sha256??null]:['unmanaged',id]);
|
||||
lease=displayCache.acquire(displayOwner,key,async signal=>{
|
||||
let bytes=adoptedFiles.get(id)??fileWrites.get(id)??await options.storage.readFile(leader,id);
|
||||
let bytes=adoptedFiles.get(id)?.bytes??fileWrites.get(id)??await options.storage.readFile(leader,id);
|
||||
if(!bytes&&binding&&depot)bytes=await depot.read(leader,binding,signal);
|
||||
assertDisplayIdentity();if(signal.aborted)throw fault('display_aborted');
|
||||
if(orphan&&(!bytes||bytes.size!==orphan.byteLength||bytes.type!==orphan.mimeType||await sourceDigest(bytes,signal)!==orphan.digest))throw fault('file_inventory_changed',[id]);
|
||||
|
||||
Reference in New Issue
Block a user