Skip to content

Commit 2cb3865

Browse files
fix: remove prefetch system - uploads start immediately instead of blocking on SAS batch
Prefetch was blocking all uploads until all SAS URLs were pre-fetched (21 sequential API calls for 2K files). Uploads showed 0 progress. Removed the prefetch + cache system entirely. Each file now gets its SAS URL on-demand when its turn comes, with 12 concurrent uploads starting immediately.
1 parent c0b670a commit 2cb3865

1 file changed

Lines changed: 3 additions & 65 deletions

File tree

client/src/stores/uploadStore.ts

Lines changed: 3 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ interface UploadBatch {
4242
const MAX_CONCURRENT = 12;
4343
const MAX_RETRIES = 3;
4444
const INITIAL_RETRY_DELAY = 1000;
45-
const BATCH_SAS_SIZE = 100; // Request SAS URLs in batches of 100
4645
const PROGRESS_THROTTLE_MS = 500;
4746
const COMPLETE_FLUSH_INTERVAL = 2000; // Flush complete queue every 2 seconds
4847
const COMPLETE_BATCH_SIZE = 20; // Max items per complete-batch call
@@ -282,32 +281,6 @@ export const useUploadStore = create<UploadStore>((set, get) => {
282281
}
283282
};
284283

285-
// Pre-request SAS URLs in batches to reduce round-trips
286-
const prefetchSasUrls = async (items: UploadItem[]): Promise<Map<string, { mediaId: string; uploadUrl: string }>> => {
287-
const results = new Map<string, { mediaId: string; uploadUrl: string }>();
288-
289-
// Process in batches
290-
for (let i = 0; i < items.length; i += BATCH_SAS_SIZE) {
291-
const batch = items.slice(i, i + BATCH_SAS_SIZE);
292-
const requests = batch.map(item => ({
293-
fileName: item.file.name,
294-
contentType: inferContentType(item.file),
295-
sizeBytes: item.file.size,
296-
}));
297-
298-
try {
299-
const responses = await mediaApi.requestUploadBatch(requests);
300-
for (let j = 0; j < batch.length && j < responses.length; j++) {
301-
results.set(batch[j].id, responses[j]);
302-
}
303-
} catch {
304-
// Batch endpoint not available — fall back to individual requests
305-
return results;
306-
}
307-
}
308-
return results;
309-
};
310-
311284
// Using function declaration (hoisted) so flushCompleteQueue can reference it
312285
function checkBatchComplete(batchId: string) {
313286
const state = get();
@@ -352,32 +325,6 @@ export const useUploadStore = create<UploadStore>((set, get) => {
352325
set(state => ({ batches: state.batches.filter(b => b.id !== batchId) }));
353326
}
354327

355-
// The prefetch cache: uploadItemId → { mediaId, uploadUrl }
356-
let sasCache = new Map<string, { mediaId: string; uploadUrl: string }>();
357-
358-
const uploadFileWithCache = async (item: UploadItem, attempt = 0) => {
359-
const cached = sasCache.get(item.id);
360-
if (cached) {
361-
try {
362-
updateItem(item.id, { status: 'uploading', mediaId: cached.mediaId, progress: 0 });
363-
await uploadToBlob(cached.uploadUrl, item.file, item.id);
364-
queueComplete(cached.mediaId, item.id);
365-
return;
366-
} catch (err: any) {
367-
if (attempt < MAX_RETRIES) {
368-
const delay = Math.min(INITIAL_RETRY_DELAY * Math.pow(2, attempt), 30000);
369-
updateItem(item.id, { status: 'uploading', progress: 0, error: `Retry ${attempt + 1}/${MAX_RETRIES}...` });
370-
await new Promise(r => setTimeout(r, delay));
371-
return uploadFileWithCache(item, attempt + 1);
372-
}
373-
updateItem(item.id, { status: 'error', error: err.message || 'Upload failed' });
374-
return;
375-
}
376-
}
377-
// No cache hit — fall back to individual request
378-
return uploadFile(item, attempt);
379-
};
380-
381328
return {
382329
_itemMap: new Map(),
383330
_itemOrder: [],
@@ -430,8 +377,6 @@ export const useUploadStore = create<UploadStore>((set, get) => {
430377
newOrder.push(item.id);
431378
}
432379

433-
sasCache = new Map();
434-
435380
set({
436381
_itemMap: newMap,
437382
_itemOrder: newOrder,
@@ -446,15 +391,8 @@ export const useUploadStore = create<UploadStore>((set, get) => {
446391
},
447392
});
448393

449-
// Prefetch SAS URLs then start processing — NOT before, to avoid
450-
// creating duplicate MediaItems (prefetch + individual request race)
451-
prefetchSasUrls(newItems).then(cache => {
452-
sasCache = cache;
453-
get()._processQueue();
454-
}).catch(() => {
455-
// Prefetch failed — process without cache (individual requests)
456-
get()._processQueue();
457-
});
394+
// Start uploading immediately — each file gets its own SAS URL on demand
395+
get()._processQueue();
458396
});
459397
},
460398

@@ -482,7 +420,7 @@ export const useUploadStore = create<UploadStore>((set, get) => {
482420
}
483421

484422
for (const item of queued) {
485-
uploadFileWithCache(item).then(() => {
423+
uploadFile(item).then(() => {
486424
get()._processQueue();
487425
for (const batch of get().batches) checkBatchComplete(batch.id);
488426

0 commit comments

Comments
 (0)