Skip to content

test(Spanner): add SQL Server comprehensive integration tests for Datastream to Spanner pipeline - #4230

Open
pratickchokhani wants to merge 15 commits into
GoogleCloudPlatform:mainfrom
pratickchokhani:sqlserver-live-migration-integration
Open

pratickchokhani wants to merge 15 commits into
GoogleCloudPlatform:mainfrom
pratickchokhani:sqlserver-live-migration-integration

Conversation

@pratickchokhani

Copy link
Copy Markdown
Contributor

No description provided.

@codecov

codecov Bot commented Sep 8, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 56.86%. Comparing base (306f713) to head (0688f84).
⚠️ Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
...it/gcp/cloudsql/CloudSqlServerResourceManager.java 0.00% 9 Missing ⚠️

❌ Your patch check has failed because the patch coverage (0.00%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@              Coverage Diff              @@
##               main    #4230       +/-   ##
=============================================
+ Coverage     40.97%   56.86%   +15.88%     
- Complexity      741     8085     +7344     
=============================================
  Files           210     1164      +954     
  Lines         15359    74117    +58758     
  Branches       1628     8735     +7107     
=============================================
+ Hits           6294    42144    +35850     
- Misses         8545    29170    +20625     
- Partials        520     2803     +2283     
Components Coverage Δ
spanner-templates 83.69% <ø> (∅)
spanner-import-export 72.31% <ø> (∅)
spanner-live-forward-migration 86.90% <ø> (∅)
spanner-live-reverse-replication 79.19% <ø> (∅)
spanner-bulk-migration 87.86% <ø> (∅)
gcs-spanner-dv 85.68% <ø> (∅)
Files with missing lines Coverage Δ
...urce/sqlserver/SqlServerDsToSpSourceConnector.java 100.00% <ø> (ø)
...it/gcp/cloudsql/CloudSqlServerResourceManager.java 0.00% <0.00%> (ø)

... and 978 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@pratickchokhani pratickchokhani changed the title feat(Spanner): add SQL Server comprehensive integration tests for Datastream to Spanner pipeline test(Spanner): add SQL Server comprehensive integration tests for Datastream to Spanner pipeline Sep 8, 2026
@pratickchokhani pratickchokhani added the addition New feature or request label Sep 8, 2026
@pratickchokhani
pratickchokhani force-pushed the sqlserver-live-migration-integration branch 10 times, most recently from b63ef92 to 44c9998 Compare September 9, 2026 13:54
@pratickchokhani
pratickchokhani force-pushed the sqlserver-live-migration-integration branch from 44c9998 to c7efd4a Compare October 27, 2026 09:36
…o Spanner pipeline and update timestamp conversion logic
- Rebase on main after GoogleCloudPlatform#4244 (static SQL Server container, shared datatype IT).
- Align existing SQL Server ITs with the merged approach (enableCdcOnAllTables,
  shared SQLServerITContainer, resilient teardown); base class unchanged from main.
- Add live-Datastream ports of the manifest scenarios: Events, DDL (custom
  transformation, size conversion, identity), Timezone, Session (rename/drop,
  synthetic PK), SeparateShadowTable PK-focused, SingleDF sharded (@ignore until
  migration_shard_id is populated for sqlserver sources).
- Drop ITs not listed in manifest.yaml (RetryAllDLQ, sharded RetryDLQ,
  TableAndIndexLimits) and the onboarding report.
@pratickchokhani
pratickchokhani force-pushed the sqlserver-live-migration-integration branch from c7efd4a to 7c5cd31 Compare October 27, 2026 10:17
- EventsIT: quote reserved keyword [plan] in SQL Server DDL/DML.
- SQLServerDataStreamToSpannerIT: drop manual sp_cdc_* statements from the
  schema script; enableCdcOnAllTables() owns CDC setup.
- RetryDLQIT: AllDataTypes.datetime_col -> DATETIMEOFFSET (datetime is routed
  to the DLQ), which left a second permanent event in the severe bucket.
- SeparateShadowTablePKFocusedIT: render the expected string via
  Value.string(..).toString(), which elides values longer than 36 chars.
With several ITs enabling CDC concurrently on the shared SQL Server container,
the first sp_cdc_enable_table per database deadlocks in msdb.dbo.sp_add_job
(error 1205) and SQL Server asks to rerun the transaction.

- CloudSqlServerResourceManager.enableCdcOnAllTables(): retry on deadlock with
  backoff; the cursor only visits untracked tables so reruns resume safely.
- Add enableCdcOnTable(schema, table, uniqueIndexName) with the same retry for
  tables without a primary key.
- SessionIT: enable CDC on the PK-less Books table through the resource manager
  instead of a raw EXEC in the DDL script.
Hold a static lock while enabling CDC so concurrent ITs on the shared SQL
Server container never race on msdb job creation; keep the deadlock retry as a
fallback for contention from outside the JVM.
enableCdcOnAllTables() now uses a table's first unique index as @index_name
when it has no primary key, so the separate enableCdcOnTable helper and the
capture-job helper are folded away.
Only the lock is needed for the deadlock; the PK-less Books table in the
Session IT gets a primary key on the source side instead.
… row count

The CDC wave's row count is also satisfied by transient states before the
UPDATE of id 3 is applied, which made the final assertion racy.
- DDLIT / SessionIT: wait for the updated rows, not just the row count, after
  the CDC wave (same race as EventsIT).
- RetryDLQIT: restore the 'wait for fixed rows' step from the MySQL reference
  before the final assertions.
- Pass only the generated session content in SQLServerDataStreamToSpannerIT.
- Delete Datastream streams before dropping the source database in the three
  ITs that cleaned up in the other order.
@pratickchokhani
pratickchokhani marked this pull request as ready for review October 28, 2026 05:07
@pratickchokhani
pratickchokhani requested a review from a team as a code owner October 28, 2026 05:07
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces critical stability improvements for the SQL Server resource manager by serializing CDC enablement to avoid deadlocks. Additionally, it significantly expands the test coverage for the Datastream to Spanner pipeline by adding a suite of integration tests that validate complex migration scenarios, including schema evolution, reserved keyword handling, and sharded data replication.

Highlights

  • Concurrency Fix: Added a synchronization lock (CDC_ENABLE_LOCK) in CloudSqlServerResourceManager to prevent deadlocks when enabling CDC on multiple tables concurrently.
  • New Integration Tests: Added comprehensive integration tests for SQL Server to Spanner pipeline, covering DLQ retries, reserved keywords, DDL migrations, event handling (INSERT/UPDATE/DELETE), timezone handling, sharded migrations, and shadow table database configurations.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive suite of integration tests for SQL Server to Spanner migrations using Datastream, covering various scenarios such as retry DLQ, reserved keywords, DDL conversions, events, sessions, timezones, sharded migrations, and separate shadow table databases. Additionally, it resolves a potential concurrent deadlock issue (SQL Server error 1205) in CloudSqlServerResourceManager by introducing a static lock (CDC_ENABLE_LOCK) to serialize CDC enablement. The reviewer feedback highlights a critical improvement opportunity across all the newly added integration tests: wrapping the Datastream resource manager cleanup in a try-finally block to guarantee that expensive cloud resources (like Spanner and Cloud SQL instances) are always cleaned up even if the Datastream cleanup fails.

…stead

Each CDC wait now describes the exact end state of the wave (updated rows
visible, deleted rows gone, inserted rows present), so the fixed cutover sleep
before assertions is no longer needed.
@pratickchokhani
pratickchokhani force-pushed the sqlserver-live-migration-integration branch from cb0ec64 to 82b11f0 Compare October 28, 2026 07:12
Datastream resource manager is passed first so streams are still deleted
before the CDC-enabled source database is dropped.

@dhwanilpatel dhwanilpatel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's trigger ITs couple of time to validate multiple ITs are working well against static testcontainer.

@Category({TemplateIntegrationTest.class, SkipDirectRunnerTest.class})
@TemplateIntegrationTest(DataStreamToSpanner.class)
@RunWith(JUnit4.class)
public class SQLServerDataStreamToSpannerDDLIT extends DataStreamToSpannerITBase {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think DDL IT has more ITs, can you double check with mysql DDL IT if some tests are missing from it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I omitted those because they were covered by other ITs. Have added them.

Comment on lines +147 to +149
// Insert initial data
insertSqlServerData("source_table1", 1, "Alice", 30, "Y", "2026-01-01");
insertSqlServerData("source_table2", 1, "Bob", 40, "N", "2026-01-02");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems this is only testing inserts, we will need to test updates/delete as well. Reference mysql test verifies for all operations. Let's update this accordingly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines +97 to +107
@After
public void cleanUp() {
LOG.info("Cleaning up resources...");
// Datastream streams are deleted first, before the CDC-enabled source database is dropped.
ResourceManagerUtils.cleanResources(
datastreamResourceManager,
msSqlResourceManager,
spannerResourceManager,
gcsResourceManager,
pubsubResourceManager);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From gemini : Let's check for other class as well.

Subclass @after teardown deletes live GCP/DB resources while the streaming Dataflow job is still running

  • Locations:
    • DatastreamToSpannerReservedKeywordsSqlServerIT.java:124-134
    • SQLServerDataStreamToSpannerIT.java:113-122
    • SQLServerDataStreamToSpannerTimezoneIT.java:124-134
    • SQLServerSeparateShadowTableDatabasePKFocusedIT.java:134-145
  • Issue: In JUnit 4, subclass @after methods run before superclass @after methods (TemplateTestBase.baseCleanup()). Because these classes don't set
    skipBaseCleanup = true and call tearDownBase() first, cleanUp() deletes the Datastream stream, SQL Server DB, Spanner DB, GCS objects, and Pub/Sub subscriptions
    before tearDownBase() cancels the running Dataflow job (and can invoke gcsClient.cleanupAll() twice).
  • Fix: Set skipBaseCleanup = true in @before and call tearDownBase() at the start of cleanUp() before cleaning up resource managers (matching
    OracleDataStreamToSpannerIT.java:118-145).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

*
* @throws IOException
*/
@AfterClass

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class has multiple methods which will spin up different dataflow jobs but those will be cleaned at the end of class itself. Let's clean those at the end of test itself.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is only 1 dataflow job for the whole class.

- Cancel the Dataflow job (tearDownBase) before deleting its resources in the
  per-test ITs (ReservedKeywords, SQLServerDataStreamToSpannerIT, Timezone).
- SQLServerDataStreamToSpannerIT: verify UPDATE and DELETE in addition to
  INSERT, like the MySQL reference.
- DDLIT: port DatatypeSizeReducedConversion (oversized values rejected, not
  truncated) and SequenceColumns (SQL Server SEQUENCE key default).
SQL Server pads CHAR(n) to n characters, so the 'fits' row never fit the
reduced STRING(5) column either. Keep char_column at full width and apply the
size reduction to the NVARCHAR column; drop the varbinary column from the table.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

addition New feature or request size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants