@@ -218,7 +218,7 @@ impl Chunk {
218218#[ cfg( feature = "server-logic" ) ]
219219pub mod db {
220220 use super :: * ;
221- use crate :: common:: errors:: { ChunkNotFound , DatabaseError , SubmissionNotFound , E } ;
221+ use crate :: common:: errors:: { DatabaseError , SubmissionNotFound , E } ;
222222 use crate :: db:: { Connection , True , WriterConnection } ;
223223 use axum_prometheus:: metrics:: { counter, gauge} ;
224224 use sqlx:: { query, query_as} ;
@@ -281,25 +281,18 @@ pub mod db {
281281 chunk_id : ChunkId ,
282282 output_content : Option < Vec < u8 > > ,
283283 mut conn : impl WriterConnection ,
284- ) -> Result < ( ) , E < DatabaseError , E < SubmissionNotFound , ChunkNotFound > > > {
285- let _chunk_size: Result < ChunkSize , E < DatabaseError , E < SubmissionNotFound , ChunkNotFound > > > =
286- conn. transaction ( move |mut tx| {
287- Box :: pin ( async move {
288- let completed_work =
289- complete_chunk_raw ( chunk_id, output_content, & mut tx) . await ?;
290- crate :: common:: submission:: db:: maybe_complete_submission (
291- chunk_id. submission_id ,
292- & mut tx,
293- )
294- . await
295- . map_err ( |e| match e {
296- E :: L ( e) => E :: L ( e) ,
297- E :: R ( e) => E :: R ( E :: L ( e) ) ,
298- } ) ?;
299- Ok ( completed_work. unwrap_or_default ( ) )
300- } )
284+ ) -> Result < ( ) , E < DatabaseError , SubmissionNotFound > > {
285+ conn. transaction ( move |mut tx| {
286+ Box :: pin ( async move {
287+ complete_chunk_raw ( chunk_id, output_content, & mut tx) . await ?;
288+ crate :: common:: submission:: db:: maybe_complete_submission (
289+ chunk_id. submission_id ,
290+ & mut tx,
291+ )
292+ . await
301293 } )
302- . await ;
294+ } )
295+ . await ?;
303296
304297 counter ! ( crate :: prometheus:: CHUNKS_COMPLETED_COUNTER ) . increment ( 1 ) ;
305298 Ok ( ( ) )
@@ -311,17 +304,16 @@ pub mod db {
311304 chunk_id : ChunkId ,
312305 output_content : Option < Vec < u8 > > ,
313306 mut tx : impl WriterConnection < Transaction = True > ,
314- ) -> sqlx:: Result < Option < ChunkSize > > {
307+ ) -> sqlx:: Result < ( ) > {
315308 let now = chrono:: prelude:: Utc :: now ( ) ;
316- query ! (
309+ let rows_affected = query ! (
317310 "
318311 INSERT INTO chunks_completed
319312 (submission_id, chunk_index, output_content, completed_at)
320313 SELECT submission_id, chunk_index, $1, julianday($2) FROM chunks
321314 WHERE chunks.submission_id = $3 AND chunks.chunk_index = $4;
322315
323- DELETE FROM chunks WHERE chunks.submission_id = $5 AND chunks.chunk_index = $6
324- RETURNING submission_id, chunk_index;
316+ DELETE FROM chunks WHERE chunks.submission_id = $5 AND chunks.chunk_index = $6;
325317 " ,
326318 output_content,
327319 now,
@@ -330,26 +322,40 @@ pub mod db {
330322 chunk_id. submission_id,
331323 chunk_id. chunk_index,
332324 )
333- . fetch_one ( tx. get_inner ( ) )
334- . await ?;
335- // Defense in depth: Above query should never be called twice on the same chunk.
336- // If it _does_ happen, it means that either a consumer is attempting a chunk they didn't reserve,
337- // or we gave out the same reservation twice.
325+ . execute ( tx. get_inner ( ) )
326+ . await ?
327+ . rows_affected ( ) as i64 ;
328+ // Defense in depth: Above query could be called twice on the same chunk. For instance,
329+ // when the server was restarted and the reservations are forgotten, and the same chunk
330+ // was reserved again.
331+ //
332+ // By using the rows affected to update `chunks_done` we ensure that we never mess up the
333+ // submission's `chunks_done` counter.
338334 //
339- // By returning early if the chunk was not found,
340- // we ensure that even in these situations
341- // we never mess up the submission's `chunks_done` counter .
335+ // In addition, cancelling or pausing a submission while a chunk is reserved also results
336+ // in `rows_affected` being 0. Which is fine, because cancelled submissions count as failed,
337+ // and for paused submissions we will retry the chunk when it becomes unpaused .
342338 //
343339 // (Not doing that resulted in a hard-to-track-down bug in the past.
344340 // https://github.com/channable/opsqueue/issues/76
345341 // )
342+
343+ if rows_affected == 0 {
344+ tracing:: warn!(
345+ "Could not complete chunk because it was either: completed before,\
346+ or cancelled, or paused. Ignoring."
347+ ) ;
348+ }
349+
346350 sqlx:: query_scalar!(
347- "UPDATE submissions SET chunks_done = chunks_done + 1 WHERE submissions.id = $1 RETURNING submissions.chunk_size;" ,
351+ "UPDATE submissions SET chunks_done = chunks_done + $1 WHERE submissions.id = $2;" ,
352+ rows_affected,
348353 chunk_id. submission_id,
349354 )
350- . fetch_one ( tx. get_inner ( ) )
351- . await
352- . map ( |opt| opt. map ( ChunkSize ) )
355+ . execute ( tx. get_inner ( ) )
356+ . await ?;
357+
358+ Ok ( ( ) )
353359 }
354360
355361 #[ tracing:: instrument( skip( conn) ) ]
0 commit comments