Skip to content

Commit d35c189

Browse files
Optimistic presigned urls (#1187)
* initial pass * improved * fix * cleanup * cleanup code a bit * thanks CodeRabbit * fix comment
1 parent a420f8f commit d35c189

File tree

3 files changed

+25
-10
lines changed

3 files changed

+25
-10
lines changed

apps/desktop/src-tauri/src/api.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ pub async fn upload_multipart_presign_part(
5252
video_id: &str,
5353
upload_id: &str,
5454
part_number: u32,
55-
md5_sum: &str,
5655
) -> Result<String, AuthedApiError> {
5756
#[derive(Deserialize)]
5857
#[serde(rename_all = "camelCase")]
@@ -68,7 +67,6 @@ pub async fn upload_multipart_presign_part(
6867
"videoId": video_id,
6968
"uploadId": upload_id,
7069
"partNumber": part_number,
71-
"md5Sum": md5_sum
7270
}))
7371
})
7472
.await

apps/desktop/src-tauri/src/upload.rs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use cap_project::{RecordingMeta, S3UploadMeta, UploadMeta};
1414
use cap_utils::spawn_actor;
1515
use ffmpeg::ffi::AV_TIME_BASE;
1616
use flume::Receiver;
17-
use futures::{Stream, StreamExt, TryStreamExt, stream};
17+
use futures::{Stream, StreamExt, TryStreamExt, future::join, stream};
1818
use image::{ImageReader, codecs::jpeg::JpegEncoder};
1919
use reqwest::StatusCode;
2020
use serde::{Deserialize, Serialize};
@@ -629,17 +629,32 @@ fn multipart_uploader(
629629
try_stream! {
630630
let mut stream = pin!(stream);
631631
let mut prev_part_number = None;
632+
let mut expected_part_number = 1u32;
633+
634+
loop {
635+
let (Some(item), presigned_url) = join(
636+
stream.next(),
637+
// We generate the presigned URL ahead of time for the part we expect to come next.
638+
// If it's not the chunk that actually comes next we just throw it out.
639+
// This means if the filesystem takes a while for the recording to reach previous total + CHUNK_SIZE, which is the common case, we aren't just doing nothing.
640+
api::upload_multipart_presign_part(&app, &video_id, &upload_id, expected_part_number)
641+
).await else {
642+
break;
643+
};
644+
let mut presigned_url = presigned_url?;
645+
632646

633-
while let Some(item) = stream.next().await {
634647
let Chunk { total_size, part_number, chunk } = item.map_err(|err| format!("uploader/part/{:?}/fs: {err:?}", prev_part_number.map(|p| p + 1)))?;
635648
trace!("Uploading chunk {part_number} ({} bytes) for video {video_id:?}", chunk.len());
636649
prev_part_number = Some(part_number);
637650
let md5_sum = base64::encode(md5::compute(&chunk).0);
638651
let size = chunk.len();
639652

640-
let presigned_url =
641-
api::upload_multipart_presign_part(&app, &video_id, &upload_id, part_number, &md5_sum)
642-
.await?;
653+
// We prefetched for the wrong chunk. Let's try again.
654+
if expected_part_number != part_number {
655+
presigned_url = api::upload_multipart_presign_part(&app, &video_id, &upload_id, part_number)
656+
.await?
657+
}
643658

644659
trace!("Uploading part {part_number}");
645660

@@ -671,6 +686,8 @@ fn multipart_uploader(
671686
size,
672687
total_size
673688
};
689+
690+
expected_part_number = part_number + 1;
674691
}
675692

676693
debug!("Completed multipart upload for {video_id:?} in {:?}", start.elapsed());

apps/web/app/api/upload/[...route]/multipart.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,18 +141,19 @@ app.post(
141141
.object({
142142
uploadId: z.string(),
143143
partNumber: z.number(),
144-
md5Sum: z.string(),
145144
})
146145
.and(
147146
z.union([
148147
z.object({ videoId: z.string() }),
149148
// deprecated
150149
z.object({ fileKey: z.string() }),
150+
// deprecated
151+
// z.object({ md5Sum: z.string() }),
151152
]),
152153
),
153154
),
154155
async (c) => {
155-
const { uploadId, partNumber, md5Sum, ...body } = c.req.valid("json");
156+
const { uploadId, partNumber, ...body } = c.req.valid("json");
156157
const user = c.get("user");
157158

158159
const fileKey = parseVideoIdOrFileKey(user.id, {
@@ -174,7 +175,6 @@ app.post(
174175
fileKey,
175176
uploadId,
176177
partNumber,
177-
{ ContentMD5: md5Sum },
178178
);
179179

180180
return presignedUrl;

0 commit comments

Comments
 (0)