diff --git a/src/pipeline/pass_cross_repo.c b/src/pipeline/pass_cross_repo.c index 09384a8e9..1f57d1c25 100644 --- a/src/pipeline/pass_cross_repo.c +++ b/src/pipeline/pass_cross_repo.c @@ -33,7 +33,6 @@ enum { CR_PATH_BUF = 4096, CR_QN_BUF = 512, CR_PROPS_BUF = 2048, - CR_MAX_EDGES = 4096, CR_DB_EXT_LEN = 3, /* strlen(".db") */ CR_INIT_CAP = 32, CR_MAX_PROJECTS = 4096, @@ -481,9 +480,9 @@ static bool cr_path_matches_template(const char *concrete, const char *templ) { * the route QN into out_qn and return the handler id; returns 0 on no match. * * COST: this scans every Route node of the target project once per unmatched - * HTTP_CALLS edge — O(calls × routes) per project pair. Acceptable while both - * factors stay small (calls are capped at CR_MAX_EDGES and it only runs for - * edges the exact lookup missed); revisit with a prepared template index if + * HTTP_CALLS edge — O(calls × routes) per project pair. It only runs for edges + * the exact lookup missed; no row count stops a scan (a cap decided which + * calls were matched at all); revisit with a prepared template index if * cross-repo matching ever shows up in profiles. (#523) */ static int64_t find_route_handler_fuzzy(cbm_store_t *target_store, const char *concrete_path, const char *method, char *out_qn, size_t out_qn_sz, @@ -504,11 +503,8 @@ static int64_t find_route_handler_fuzzy(cbm_store_t *target_store, const char *c return 0; } int64_t found = 0; - int scanned = 0; int step_rc = SQLITE_DONE; - while (scanned < CR_MAX_EDGES && !cr_cancel_requested(ctx) && - (step_rc = sqlite3_step(s)) == SQLITE_ROW) { - scanned++; + while (!cr_cancel_requested(ctx) && (step_rc = sqlite3_step(s)) == SQLITE_ROW) { const char *qn = (const char *)sqlite3_column_text(s, 0); if (!qn || strncmp(qn, "__route__", CR_ROUTE_PREFIX_LEN) != 0) { continue; @@ -550,7 +546,7 @@ static int64_t find_route_handler_fuzzy(cbm_store_t *target_store, const char *c break; } } - if (!cr_cancel_requested(ctx) && scanned < CR_MAX_EDGES && step_rc != SQLITE_DONE) { + if (!cr_cancel_requested(ctx) && step_rc != SQLITE_DONE) { *failed = true; } if (sqlite3_finalize(s) != SQLITE_OK) { @@ -713,12 +709,9 @@ static cr_match_result_t match_http_routes(cbm_store_t *src_store, const char *s } int count = 0; - int scanned = 0; int step_rc = SQLITE_DONE; bool failed = false; - while (scanned < CR_MAX_EDGES && !cr_cancel_requested(ctx) && - (step_rc = sqlite3_step(s)) == SQLITE_ROW) { - scanned++; + while (!cr_cancel_requested(ctx) && (step_rc = sqlite3_step(s)) == SQLITE_ROW) { int64_t caller_id = sqlite3_column_int64(s, 0); int64_t route_id = sqlite3_column_int64(s, SKIP_ONE); const char *props = (const char *)sqlite3_column_text(s, PAIR_LEN); @@ -772,7 +765,7 @@ static cr_match_result_t match_http_routes(cbm_store_t *src_store, const char *s count++; } - if (!failed && !cr_cancel_requested(ctx) && scanned < CR_MAX_EDGES && step_rc != SQLITE_DONE) { + if (!failed && !cr_cancel_requested(ctx) && step_rc != SQLITE_DONE) { failed = true; } if (sqlite3_finalize(s) != SQLITE_OK) { @@ -804,12 +797,9 @@ static cr_match_result_t match_async_routes(cbm_store_t *src_store, const char * } int count = 0; - int scanned = 0; int step_rc = SQLITE_DONE; bool failed = false; - while (scanned < CR_MAX_EDGES && !cr_cancel_requested(ctx) && - (step_rc = sqlite3_step(s)) == SQLITE_ROW) { - scanned++; + while (!cr_cancel_requested(ctx) && (step_rc = sqlite3_step(s)) == SQLITE_ROW) { int64_t caller_id = sqlite3_column_int64(s, 0); int64_t route_id = sqlite3_column_int64(s, SKIP_ONE); const char *props = (const char *)sqlite3_column_text(s, PAIR_LEN); @@ -854,7 +844,7 @@ static cr_match_result_t match_async_routes(cbm_store_t *src_store, const char * } count++; } - if (!failed && !cr_cancel_requested(ctx) && scanned < CR_MAX_EDGES && step_rc != SQLITE_DONE) { + if (!failed && !cr_cancel_requested(ctx) && step_rc != SQLITE_DONE) { failed = true; } if (sqlite3_finalize(s) != SQLITE_OK) { @@ -969,12 +959,9 @@ static cr_match_result_t match_channels(cbm_store_t *src_store, const char *src_ } int count = 0; - int scanned = 0; int step_rc = SQLITE_DONE; bool failed = false; - while (scanned < CR_MAX_EDGES && !cr_cancel_requested(ctx) && - (step_rc = sqlite3_step(s)) == SQLITE_ROW) { - scanned++; + while (!cr_cancel_requested(ctx) && (step_rc = sqlite3_step(s)) == SQLITE_ROW) { const char *channel_name = (const char *)sqlite3_column_text(s, SKIP_ONE); const char *channel_qn = (const char *)sqlite3_column_text(s, PAIR_LEN); if (!channel_name || !channel_qn) { @@ -1007,7 +994,7 @@ static cr_match_result_t match_channels(cbm_store_t *src_store, const char *src_ count++; } } - if (!failed && !cr_cancel_requested(ctx) && scanned < CR_MAX_EDGES && step_rc != SQLITE_DONE) { + if (!failed && !cr_cancel_requested(ctx) && step_rc != SQLITE_DONE) { failed = true; } if (sqlite3_finalize(s) != SQLITE_OK) { @@ -1084,12 +1071,9 @@ static cr_match_result_t match_typed_routes(cbm_store_t *src_store, const char * } int count = 0; - int scanned = 0; int step_rc = SQLITE_DONE; bool failed = false; - while (scanned < CR_MAX_EDGES && !cr_cancel_requested(ctx) && - (step_rc = sqlite3_step(s)) == SQLITE_ROW) { - scanned++; + while (!cr_cancel_requested(ctx) && (step_rc = sqlite3_step(s)) == SQLITE_ROW) { int64_t caller_id = sqlite3_column_int64(s, 0); int64_t route_id = sqlite3_column_int64(s, SKIP_ONE); const char *props = (const char *)sqlite3_column_text(s, PAIR_LEN); @@ -1139,7 +1123,7 @@ static cr_match_result_t match_typed_routes(cbm_store_t *src_store, const char * } count++; } - if (!failed && !cr_cancel_requested(ctx) && scanned < CR_MAX_EDGES && step_rc != SQLITE_DONE) { + if (!failed && !cr_cancel_requested(ctx) && step_rc != SQLITE_DONE) { failed = true; } if (sqlite3_finalize(s) != SQLITE_OK) { diff --git a/tests/test_cross_repo.c b/tests/test_cross_repo.c index 23d74fb5b..9d318f8c0 100644 --- a/tests/test_cross_repo.c +++ b/tests/test_cross_repo.c @@ -574,7 +574,10 @@ static bool cross_repo_seed_bounded_scan(const cross_repo_fixture_t *fixture, return ok; } -TEST(cross_repo_scan_bound_counts_examined_rows_not_matches) { +/* No row count decides which calls are matched: the scans stopped after 4,096 + * rows, so a call examined after 4,096 others never got its CROSS_HTTP_CALLS + * edge even though the target serves its route. */ +TEST(cross_repo_matches_calls_past_4096_rows) { cross_repo_fixture_t fixture; bool setup = cross_repo_fixture_begin(&fixture) && cross_repo_seed_bounded_scan(&fixture, "bounded-source", "bounded-target"); @@ -589,7 +592,7 @@ TEST(cross_repo_scan_bound_counts_examined_rows_not_matches) { ASSERT_FALSE(result.failed); ASSERT_EQ(result.projects_scanned, 1); - ASSERT_EQ(result.http_edges, 0); + ASSERT_EQ(result.http_edges, 1); PASS(); } @@ -952,7 +955,7 @@ SUITE(cross_repo) { RUN_TEST(cross_repo_named_pre768_target_fails_before_cleanup_issue2133); RUN_TEST(cross_repo_status_distinguishes_never_run_from_ran_without_links); RUN_TEST(cross_repo_wildcard_reports_skipped_pre768_store); - RUN_TEST(cross_repo_scan_bound_counts_examined_rows_not_matches); + RUN_TEST(cross_repo_matches_calls_past_4096_rows); RUN_TEST(cross_repo_propagates_delete_failure); RUN_TEST(cross_repo_failed_bidirectional_insert_is_not_counted); RUN_TEST(cross_repo_cancel_mid_run_keeps_completed_target_and_stops_before_later_target);