@@ -272,6 +272,34 @@ int db_send_await_converge (sqlite3 *db, int max_attempts, int delay_ms) {
272272 return SQLITE_ERROR ;
273273}
274274
275+ // Bring a fresh receiver up to date with the tenant's accumulated history before a
276+ // capped-receive test: send a sentinel row from the sender, then drain (uncapped)
277+ // until it arrives. A fresh site's first check makes the server spool the ENTIRE
278+ // tenant history — which grows with every run — answering 202 while it prepares, and
279+ // a 202 yields rows=0/complete=1, indistinguishable from "already drained", so only
280+ // the sentinel's arrival proves the replay actually finished. This keeps the capped
281+ // attempt budgets O(this test's batch) instead of O(tenant history).
282+ int chunked_receiver_catch_up (sqlite3 * sender , sqlite3 * receiver , const char * sentinel_id ) {
283+ char sql [512 ];
284+ snprintf (sql , sizeof (sql ),
285+ "INSERT INTO chunked_payload_items (id, body) VALUES ('%s', 'sentinel');" , sentinel_id );
286+ int rc = db_exec (sender , sql ); if (rc != SQLITE_OK ) return rc ;
287+ rc = db_send_ok (sender ); if (rc != SQLITE_OK ) return rc ;
288+
289+ for (int i = 0 ; i < 120 ; i ++ ) {
290+ int matches = 0 ;
291+ rc = db_exec (receiver , "SELECT cloudsync_network_receive_changes();" );
292+ if (rc != SQLITE_OK ) return rc ;
293+ snprintf (sql , sizeof (sql ),
294+ "SELECT COUNT(*) FROM chunked_payload_items WHERE id='%s';" , sentinel_id );
295+ rc = db_select_int (receiver , sql , & matches ); if (rc != SQLITE_OK ) return rc ;
296+ if (matches == 1 ) return SQLITE_OK ;
297+ sqlite3_sleep (500 );
298+ }
299+ printf ("Error: receiver did not catch up with tenant history (sentinel %s missing).\n" , sentinel_id );
300+ return SQLITE_ERROR ;
301+ }
302+
275303int integration_network_init_base (sqlite3 * db , const char * database_id , char * network_init , size_t network_init_len ) {
276304 if (!database_id ) {
277305 fprintf (stderr , "Error: integration database ID not set.\n" );
@@ -1013,6 +1041,7 @@ int test_chunked_payload_capped_receive(void) {
10131041 sqlite3 * receiver = NULL ;
10141042 char network_init [1024 ];
10151043 char batch_id [UUID_STR_MAXLEN ];
1044+ char sentinel_id [UUID_STR_MAXLEN ];
10161045 char sql [1024 ];
10171046 bool found = false;
10181047 bool observed_capped_partial = false; // saw chunks==1 && complete=0 from a receive(1) call
@@ -1032,6 +1061,14 @@ int test_chunked_payload_capped_receive(void) {
10321061 if (rc != SQLITE_OK ) goto cleanup ;
10331062
10341063 cloudsync_uuid_v7_string (batch_id , true);
1064+ cloudsync_uuid_v7_string (sentinel_id , true);
1065+
1066+ // Replay accumulated tenant history up front so the 80x1-chunk budget below only
1067+ // has to fetch this test's batch (see chunked_receiver_catch_up).
1068+ rc = chunked_receiver_catch_up (sender , receiver , sentinel_id );
1069+ if (rc != SQLITE_OK ) goto cleanup ;
1070+ cleanup_remote_rows = true;
1071+
10351072 rc = db_exec (sender , "SELECT cloudsync_set('payload_max_chunk_size', '262144');" ); if (rc != SQLITE_OK ) goto cleanup ;
10361073 snprintf (sql , sizeof (sql ),
10371074 "WITH RECURSIVE c(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM c WHERE i < %d) "
@@ -1043,7 +1080,6 @@ int test_chunked_payload_capped_receive(void) {
10431080 rc = db_expect_min (sender , "SELECT COUNT(*) FROM cloudsync_payload_chunks();" , 2 ); if (rc != SQLITE_OK ) goto cleanup ;
10441081
10451082 rc = db_send_ok (sender ); if (rc != SQLITE_OK ) goto cleanup ;
1046- cleanup_remote_rows = true;
10471083
10481084 for (int attempt = 0 ; attempt < 80 ; ++ attempt ) {
10491085 int chunks = 0 , complete = 0 , matches = 0 ;
@@ -1098,7 +1134,7 @@ int test_chunked_payload_capped_receive(void) {
10981134
10991135cleanup :
11001136 if (cleanup_remote_rows && sender ) {
1101- snprintf (sql , sizeof (sql ), "DELETE FROM chunked_payload_items WHERE id LIKE '%s-%%';" , batch_id );
1137+ snprintf (sql , sizeof (sql ), "DELETE FROM chunked_payload_items WHERE id LIKE '%s-%%' OR id='%s' ;" , batch_id , sentinel_id );
11021138 if (db_exec (sender , sql ) == SQLITE_OK ) {
11031139 db_exec (sender , "SELECT cloudsync_network_send_changes();" );
11041140 }
@@ -1116,6 +1152,7 @@ int test_chunked_payload_batched_receive(void) {
11161152 sqlite3 * receiver = NULL ;
11171153 char network_init [1024 ];
11181154 char batch_id [UUID_STR_MAXLEN ];
1155+ char sentinel_id [UUID_STR_MAXLEN ];
11191156 char sql [1024 ];
11201157 bool found = false;
11211158 bool observed_batched_partial = false; // saw chunks==2 && complete=0 from receive(2)
@@ -1128,6 +1165,14 @@ int test_chunked_payload_batched_receive(void) {
11281165 if (rc != SQLITE_OK ) goto cleanup ;
11291166
11301167 cloudsync_uuid_v7_string (batch_id , true);
1168+ cloudsync_uuid_v7_string (sentinel_id , true);
1169+
1170+ // Replay accumulated tenant history up front so the capped budget below only has
1171+ // to fetch this test's batch (see chunked_receiver_catch_up).
1172+ rc = chunked_receiver_catch_up (sender , receiver , sentinel_id );
1173+ if (rc != SQLITE_OK ) goto cleanup ;
1174+ cleanup_remote_rows = true;
1175+
11311176 rc = db_exec (sender , "SELECT cloudsync_set('payload_max_chunk_size', '262144');" ); if (rc != SQLITE_OK ) goto cleanup ;
11321177 snprintf (sql , sizeof (sql ),
11331178 "WITH RECURSIVE c(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM c WHERE i < %d) "
@@ -1139,7 +1184,6 @@ int test_chunked_payload_batched_receive(void) {
11391184 rc = db_expect_min (sender , "SELECT COUNT(*) FROM cloudsync_payload_chunks();" , 3 ); if (rc != SQLITE_OK ) goto cleanup ;
11401185
11411186 rc = db_send_ok (sender ); if (rc != SQLITE_OK ) goto cleanup ;
1142- cleanup_remote_rows = true;
11431187
11441188 for (int attempt = 0 ; attempt < 80 ; ++ attempt ) {
11451189 int chunks = 0 , complete = 0 , matches = 0 ;
@@ -1186,7 +1230,7 @@ int test_chunked_payload_batched_receive(void) {
11861230
11871231cleanup :
11881232 if (cleanup_remote_rows && sender ) {
1189- snprintf (sql , sizeof (sql ), "DELETE FROM chunked_payload_items WHERE id LIKE '%s-%%';" , batch_id );
1233+ snprintf (sql , sizeof (sql ), "DELETE FROM chunked_payload_items WHERE id LIKE '%s-%%' OR id='%s' ;" , batch_id , sentinel_id );
11901234 if (db_exec (sender , sql ) == SQLITE_OK ) {
11911235 db_exec (sender , "SELECT cloudsync_network_send_changes();" );
11921236 }
0 commit comments