fixed test flakiness in replication tests#2943
Conversation
… replicated postgres db with no writes
|
SummaryCoverage spans database startup and authentication, successful and failed connection readiness, terminal error handling, retry behavior for status queries, and primary/standby availability and failover workflows. It exercises both normal operation and edge cases involving missing databases, unavailable servers, misleading query text, and unsupported replication behavior. Safe to merge — the only failure attributable to this PR is a minor query-classification issue that can unnecessarily delay some error responses, without evidence of data loss or security impact. Separate high-severity replication and failover findings are not caused by this PR and remain caveats for follow-up. Tests run by ItoAdditional Findings DetailsThese findings are unrelated to the current changes but were observed during testing. 🟠 Primary rows do not reach standby
Evidence Package🟠 Cluster failover role transition is unavailable
Evidence PackageTip Reply with @itoqa to send us feedback on this test run. |
| // replication status query to converge to its expected result. | ||
| // Retries are quite forgiving in these tests because the default `postgres` database in most of these tests is never | ||
| // written to, which means it get replicated async by a background thread, rather than by a user write. | ||
| const statusEventualConsistencyAttempts = 300 |
There was a problem hiding this comment.
Unrelated query text inflates the replication retry budget
What failed: Nine of ten STATUS-2 subtests failed. Queries such as select 1 -- see dolt_cluster_status, select 'dolt_cluster_status' as label, and select * from dolt_cluster_status_archive were classified as status queries, and the comment-only case showed the retry budget changing from 5 to 300.
Impact · Steps · Stub / mock · Analysis · Why this is likely a bug
- Severity: Minor
- Impact: Queries whose comments, string literals, or similarly named identifiers contain dolt_cluster_status can wait about 15 seconds before reporting a persistent error. The affected query eventually reports the error, with no evidence of data loss, corruption, or security exposure.
- Steps to Reproduce:
- Run the STATUS-2 Go test with go test -run TestSTATUS2 -count=1 -v in integration-tests/go-sql-server-driver.
- Pass an ordinary query containing dolt_cluster_status only in a SQL comment, string literal, or unrelated identifier, with RetryAttempts set to 5.
- Observe that observesReplicationStatus returns true and RunQuery uses 300 attempts rather than the configured budget.
- Stub / mock content: No stubs, mocks, or bypasses were applied for this test in the recorded run.
- Code Analysis: The PR-added observesReplicationStatus implementation at lines 652-655 lowercases q.Query and performs an unrestricted strings.Contains match, so it does not distinguish SQL comments or literals from referenced tables and does not enforce identifier boundaries. RunQuery at lines 640-645 consumes that result and promotes attempts below statusEventualConsistencyAttempts, which is 300 at line 637. The smallest practical fix is to parse or sanitize SQL sufficiently to remove comments and literals, then match the exact dolt_cluster_status identifier or referenced relation rather than an arbitrary substring.
- Why this is likely a bug: The failing cases are deterministic and exercise ordinary SQL text that is not a reference to the dolt_cluster_status system table, while the source directly shows that any occurrence of the text triggers the status retry path. This causes a measurable, user-visible delay in the integration driver's normal error reporting and is not explained by setup or environment behavior.
Relevant code
integration-tests/go-sql-server-driver/testdef.go:637-645
const statusEventualConsistencyAttempts = 300; RunQuery promotes attempts to this value when observesReplicationStatus(q) is true.integration-tests/go-sql-server-driver/testdef.go:652-655
func observesReplicationStatus(q driver.Query) bool { return q.Query != "" && strings.Contains(strings.ToLower(q.Query), "dolt_cluster_status") }Evidence Package
Copy prompt for an agent
Ito QA identified the following failure during automated PR testing. Please investigate and propose a fix.
**Minor severity — Unrelated query text inflates the replication retry budget**
**What failed:** Nine of ten STATUS-2 subtests failed. Queries such as select 1 -- see dolt_cluster_status, select 'dolt_cluster_status' as label, and select * from dolt_cluster_status_archive were classified as status queries, and the comment-only case showed the retry budget changing from 5 to 300.
- **Impact:** Queries whose comments, string literals, or similarly named identifiers contain dolt_cluster_status can wait about 15 seconds before reporting a persistent error. The affected query eventually reports the error, with no evidence of data loss, corruption, or security exposure.
- **Steps to reproduce:**
1. Run the STATUS-2 Go test with go test -run TestSTATUS2 -count=1 -v in integration-tests/go-sql-server-driver.
2. Pass an ordinary query containing dolt_cluster_status only in a SQL comment, string literal, or unrelated identifier, with RetryAttempts set to 5.
3. Observe that observesReplicationStatus returns true and RunQuery uses 300 attempts rather than the configured budget.
- **Stub / mock content:** No stubs, mocks, or bypasses were applied for this test in the recorded run.
- **Code analysis:** The PR-added observesReplicationStatus implementation at lines 652-655 lowercases q.Query and performs an unrestricted strings.Contains match, so it does not distinguish SQL comments or literals from referenced tables and does not enforce identifier boundaries. RunQuery at lines 640-645 consumes that result and promotes attempts below statusEventualConsistencyAttempts, which is 300 at line 637. The smallest practical fix is to parse or sanitize SQL sufficiently to remove comments and literals, then match the exact dolt_cluster_status identifier or referenced relation rather than an arbitrary substring.
- **Why this is likely a bug:** The failing cases are deterministic and exercise ordinary SQL text that is not a reference to the dolt_cluster_status system table, while the source directly shows that any occurrence of the text triggers the status retry path. This causes a measurable, user-visible delay in the integration driver's normal error reporting and is not explained by setup or environment behavior.
**Relevant code:**
`integration-tests/go-sql-server-driver/testdef.go:637-645`
~~~go
const statusEventualConsistencyAttempts = 300; RunQuery promotes attempts to this value when observesReplicationStatus(q) is true.
~~~
`integration-tests/go-sql-server-driver/testdef.go:652-655`
~~~go
func observesReplicationStatus(q driver.Query) bool { return q.Query != "" && strings.Contains(strings.ToLower(q.Query), "dolt_cluster_status") }
~~~
|
|
Diff SummaryThe run covers database query behavior across recursive graph traversal, type and array handling, replication, failover, startup readiness, and malformed-input rejection. It exercises both normal flows and edge cases such as cycles, self-loops, nulls, invalid declarations, changing replication lag, and unavailable peers. Safe to merge — the PR-attributable issue is minor and limited to unnecessary retry delays for unrelated SQL containing a status-table name, without incorrect results, data loss, or security impact. The broader functional and edge-case coverage is healthy, with this classification defect remaining as a caveat. Tests run by ItoTests that are no longer relevantBelow are tests that previously ran and are no longer relevant:
Tip Reply with @itoqa to send us feedback on this test run. |
Footnotes
|
|
Diff SummaryThe run covered database startup and readiness recovery, normal and failing query execution, replication and failover, and graceful role changes under both caught-up and unavailable conditions. It exercised happy paths, boundary cases, and error-handling behavior across connection setup and cluster transitions. Merge with caution — this PR introduces a medium-severity correctness risk in the replication transition barrier, which can treat missing status information as proof that data is synchronized. An unrelated minor retry-classification issue is also present but is not attributable to this change. Tests run by ItoAdditional Findings DetailsThese findings are unrelated to the current changes but were observed during testing. ⚪ Unrelated SQL receives status-query retries
Evidence PackageTip Reply with @itoqa to send us feedback on this test run. |
| # is deterministically about the standby count rather than about in-flight replication. | ||
| # Status queries are auto-retried by the test runner. | ||
| - exec: 'use dolt_cluster' | ||
| - query: "select count(*) as not_caught_up from dolt_cluster_status where replication_lag_millis is null or replication_lag_millis <> 0" |
There was a problem hiding this comment.
🆕 New Failure: identified in this diff run
Omitted status rows bypass the convergence barrier
What failed: The barrier checks only whether present rows have nonzero or NULL lag. It does not verify that every expected database has a status row, so an omitted database is indistinguishable from a fully caught-up set.
Impact · Steps · Stub / mock · Analysis · Why this is likely a bug
- Severity: Medium
- Impact: A cluster transition can be treated as complete while a database with missing replication status is not actually confirmed caught up, which may leave users with stale or unavailable data after the transition.
- Steps to Reproduce:
- Start a primary and standby cluster with at least one expected database absent from dolt_cluster_status.
- Run select count(*) as not_caught_up from dolt_cluster_status where replication_lag_millis is null or replication_lag_millis <> 0.
- Observe that an empty status result returns 0, the same value returned when every reported database has zero lag.
- Allow the subsequent graceful role transition to use that zero count as its quiescence proof.
- Stub / mock content: The cluster replication scenarios are marked skip because cluster replication is not implemented in this repository; no mocks, route interceptions, or bypasses were applied to the STATUS-9 contract check.
- Code Analysis: The PR-added barrier at integration-tests/go-sql-server-driver/tests/sql-server-cluster.yaml:991-994 selects count(*) from dolt_cluster_status filtered to NULL or nonzero replication_lag_millis and expects [["0"]]. With no status rows, the aggregate also returns 0, so the retry loop can accept missing coverage as convergence. The surrounding testdef.go:637-643 comments define these queries as quiescence barriers before graceful transitions, confirming that the result is used as a readiness proof. The repository notes that the cluster YAML is skipped because cluster replication is not implemented yet, so the supplied STATUS-9 run documents the contract but does not provide an end-to-end production reproduction. A targeted fix is to require the expected database set to be present and caught up, rather than relying on the filtered count alone.
- Why this is likely a bug: The distinct empty and all-caught-up outcomes are demonstrated by the STATUS-9 evidence, and the query semantics make the ambiguity deterministic. Because the barrier is intended to prove that every database is caught up before a transition, accepting an omitted database is a real correctness defect; the remaining uncertainty is only that the repository's cluster scenarios are currently skipped, not that the SQL can produce the ambiguous result.
Relevant code
integration-tests/go-sql-server-driver/tests/sql-server-cluster.yaml:991-994
- query: "select count(*) as not_caught_up from dolt_cluster_status where replication_lag_millis is null or replication_lag_millis <> 0"
result:
columns: ["not_caught_up"]
rows: [["0"]]integration-tests/go-sql-server-driver/testdef.go:637-643
// statusEventualConsistencyAttempts bounds how long RunQuery waits for a cluster
// replication status query to converge to its expected result.
// ... status queries usable as explicit quiesce barriers before graceful role transitionsEvidence Package
Copy prompt for an agent
Ito QA identified the following failure during automated PR testing. Please investigate and propose a fix.
**Medium severity — Omitted status rows bypass the convergence barrier**
**What failed:** The barrier checks only whether present rows have nonzero or NULL lag. It does not verify that every expected database has a status row, so an omitted database is indistinguishable from a fully caught-up set.
- **Impact:** A cluster transition can be treated as complete while a database with missing replication status is not actually confirmed caught up, which may leave users with stale or unavailable data after the transition.
- **Steps to reproduce:**
1. Start a primary and standby cluster with at least one expected database absent from dolt_cluster_status.
2. Run select count(*) as not_caught_up from dolt_cluster_status where replication_lag_millis is null or replication_lag_millis <> 0.
3. Observe that an empty status result returns 0, the same value returned when every reported database has zero lag.
4. Allow the subsequent graceful role transition to use that zero count as its quiescence proof.
- **Stub / mock content:** The cluster replication scenarios are marked skip because cluster replication is not implemented in this repository; no mocks, route interceptions, or bypasses were applied to the STATUS-9 contract check.
- **Code analysis:** The PR-added barrier at integration-tests/go-sql-server-driver/tests/sql-server-cluster.yaml:991-994 selects count(*) from dolt_cluster_status filtered to NULL or nonzero replication_lag_millis and expects [["0"]]. With no status rows, the aggregate also returns 0, so the retry loop can accept missing coverage as convergence. The surrounding testdef.go:637-643 comments define these queries as quiescence barriers before graceful transitions, confirming that the result is used as a readiness proof. The repository notes that the cluster YAML is skipped because cluster replication is not implemented yet, so the supplied STATUS-9 run documents the contract but does not provide an end-to-end production reproduction. A targeted fix is to require the expected database set to be present and caught up, rather than relying on the filtered count alone.
- **Why this is likely a bug:** The distinct empty and all-caught-up outcomes are demonstrated by the STATUS-9 evidence, and the query semantics make the ambiguity deterministic. Because the barrier is intended to prove that every database is caught up before a transition, accepting an omitted database is a real correctness defect; the remaining uncertainty is only that the repository's cluster scenarios are currently skipped, not that the SQL can produce the ambiguous result.
**Relevant code:**
`integration-tests/go-sql-server-driver/tests/sql-server-cluster.yaml:991-994`
~~~yaml
- query: "select count(*) as not_caught_up from dolt_cluster_status where replication_lag_millis is null or replication_lag_millis <> 0"
result:
columns: ["not_caught_up"]
rows: [["0"]]
~~~
`integration-tests/go-sql-server-driver/testdef.go:637-643`
~~~go
// statusEventualConsistencyAttempts bounds how long RunQuery waits for a cluster
// replication status query to converge to its expected result.
// ... status queries usable as explicit quiesce barriers before graceful role transitions
~~~|
Diff SummaryCoverage spans database replication and failover, role transitions, preservation of writes across multiple databases, startup and standby readiness, retry and error handling, and boundary conditions such as unavailable peers, invalid databases, lag, and concurrent connections. The exercised application behavior is broadly healthy across both normal operations and recovery or failure scenarios, with one unrelated edge-case finding in query classification. Safe to merge — the only failure is a known minor, pre-existing retry-classification issue that is explicitly unrelated to this PR and affects developer feedback rather than production data or workflows. No PR-attributable regressions, new failures, or merge-blocking behavior were identified. Tests run by ItoAdditional Findings DetailsThese findings are unrelated to the current changes but were observed during testing. ⚪ Unrelated queries receive status retry budget
Evidence PackageTip Reply with @itoqa to send us feedback on this test run. |



Caused by eventual consistency of asynchronously replicated postgres db with no writes. Added more replication tests specifically targeting the postgres DB.
Also changed an implicitly defined DB name which doesn't exist to be an error.