Skip to content

fix(migrate): improve migration status detection and prevent checksum conflicts#54

Merged
cnlangzi merged 2 commits into
mainfrom
fix/migrate
Dec 1, 2025
Merged

fix(migrate): improve migration status detection and prevent checksum conflicts#54
cnlangzi merged 2 commits into
mainfrom
fix/migrate

Conversation

@cnlangzi

@cnlangzi cnlangzi commented Dec 1, 2025

Copy link
Copy Markdown
Member

Summary by Sourcery

Improve migration status detection to distinguish between new, executed, and modified migrations and adjust migration execution accordingly.

Bug Fixes:

  • Prevent re-execution of already run migrations when their checksum matches existing records.
  • Avoid checksum conflicts by treating migrations with changed content but same identity as modified instead of new.

Enhancements:

  • Introduce explicit migration status enum to clarify and centralize migration state handling.

Tests:

  • Adjust DTC database test setup to clean up the specific database file and table before running.

@sourcery-ai

sourcery-ai Bot commented Dec 1, 2025

Copy link
Copy Markdown

Reviewer's Guide

Refactors migration detection to distinguish between new, executed, and modified migrations based on checksum and metadata, updates the migrator loop to act on the new statuses, and hardens a database-backed test by resetting its schema more thoroughly.

Sequence diagram for migration execution using new migration statuses

sequenceDiagram
    actor Operator
    participant Migrator
    participant DB as sqle_DB
    participant Tx as sqle_Tx

    Operator->>Migrator: startMigrate(ctx, db)
    Migrator->>DB: Transaction(ctx, callback)
    activate DB
    DB-->>Migrator: Tx
    activate Migrator

    loop for each Migration in version.Migrations
        Migrator->>Migrator: getMigrationStatus(Tx, version.Name, migration)
        alt status is MigrationStatusExecuted
            Migrator-->>Operator: log executed [✔]
        else status is MigrationStatusModified
            Migrator-->>Operator: log modified [!]
        else status is MigrationStatusNew
            Migrator->>Migrator: buildRotations(migration.Rotate, migration.RotateBegin, migration.RotateEnd)
            Migrator->>Tx: execute migration SQL
            Tx-->>Migrator: result
        end
    end

    DB-->>Operator: migration transaction committed
    deactivate Migrator
    deactivate DB
Loading

Class diagram for Migrator and migration status handling

classDiagram
    class MigrationStatus {
        <<enum>>
        MigrationStatusNew
        MigrationStatusExecuted
        MigrationStatusModified
    }

    class Migration {
        string Name
        int Rank
        string Checksum
        shardid_TableRotate Rotate
        time_Time RotateBegin
        time_Time RotateEnd
    }

    class Migrator {
        sqle_DB[] dbs
        string suffix
        string module
        startMigrate(ctx context_Context, db sqle_DB) error
        getMigrationStatus(tx sqle_Tx, version string, s Migration) MigrationStatus
        buildRotations(r shardid_TableRotate, begin time_Time, end time_Time) string[]
    }

    Migrator --> MigrationStatus : uses
    Migrator --> Migration : evaluates
Loading

Flow diagram for getMigrationStatus decision logic

flowchart TD
    A[start getMigrationStatus] --> B[Query sqle_migrations by checksum]
    B -->|row found| C[Return MigrationStatusExecuted]
    B -->|error not sql.ErrNoRows| D[Return MigrationStatusNew with error]
    B -->|no rows| E[Query sqle_migrations by module, version, name, rank]

    E -->|no rows| F[Return MigrationStatusNew]
    E -->|error not sql.ErrNoRows| G[Return MigrationStatusNew with error]
    E -->|row found with different checksum| H[Return MigrationStatusModified]

    C --> I[end]
    D --> I
    F --> I
    G --> I
    H --> I[end]
Loading

File-Level Changes

Change Details Files
Introduce explicit migration status enum and use it to control migration execution flow.
  • Define a MigrationStatus type with New, Executed, and Modified states using iota.
  • Replace the boolean isMigrated check in the migration loop with a getMigrationStatus call.
  • Log different markers for executed ([✔]) and modified ([!]) migrations while skipping their execution.
migrate/migrator.go
Improve migration status detection logic to avoid checksum conflicts and detect modified scripts.
  • Add getMigrationStatus method that first checks for an existing row by checksum to mark scripts as already executed.
  • On missing checksum, query by module, version, name, and rank to detect existing scripts with changed content and mark them as modified.
  • Return appropriate MigrationStatus values and propagate non-ErrNoRows errors instead of collapsing into a bool.
migrate/migrator.go
Stabilize the DTC integration test by cleaning up the specific database file and table schema before running.
  • Change removal to target dtc_1.db instead of a generic dtc_db.db file.
  • Drop the dtc_1 table if it exists before creating it to ensure a clean state across test runs.
dtc_test.go

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `dtc_test.go:13-21` </location>
<code_context>

 func TestDTCWithDB(t *testing.T) {
-	os.Remove("dtc_db.db")
+	os.Remove("dtc_1.db")

 	d, err := sql.Open("sqlite3", "file:dtc_1.db?cache=shared&mode=rwc")
 	require.NoError(t, err)

+	_, err = d.Exec("DROP TABLE IF EXISTS `dtc_1`")
+	require.NoError(t, err)
+
 	_, err = d.Exec("CREATE TABLE `dtc_1` (`id` int , `email` varchar(50),`created_at` DATETIME, PRIMARY KEY (`id`))")
 	require.NoError(t, err)

</code_context>

<issue_to_address>
**suggestion (testing):** Strengthen `TestDTCWithDB` isolation and cleanup for repeatable runs.

The added `DROP TABLE IF EXISTS` and file removal help, but the test still relies on a fixed file name (`dtc_1.db`) and may leave artifacts if it fails partway through. Please ensure cleanup is guaranteed (e.g., via `t.Cleanup`/`defer` to remove the file and drop the table) and consider using a unique or temp DB filename per run to avoid test interference and flakiness in CI or repeated local runs.

Suggested implementation:

```golang
func TestDTCWithDB(t *testing.T) {

func TestDTCWithDB(t *testing.T) {
	dbDir := t.TempDir()
	dbPath := filepath.Join(dbDir, "dtc_1.db")

	d, err := sql.Open("sqlite3", fmt.Sprintf("file:%s?cache=shared&mode=rwc", dbPath))
	require.NoError(t, err)

	t.Cleanup(func() {
		// Best-effort cleanup to keep runs isolated and avoid DB artifacts.
		_, _ = d.Exec("DROP TABLE IF EXISTS `dtc_1`")
		_ = d.Close()
		_ = os.Remove(dbPath)
	})

```

To compile and run successfully, you will also need to:
1. Ensure the import block for this test file includes:
   - `fmt`
   - `path/filepath`
   (and `os` if it is not already imported).
   For example:
   ```go
   import (
       "database/sql"
       "fmt"
       "os"
       "path/filepath"
       "testing"

       "github.com/stretchr/testify/require"
   )
   ```
2. Leave the rest of `TestDTCWithDB` (the `CREATE TABLE`, test logic, assertions, etc.) as-is below the shown snippet so it continues to use `d` as before.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread dtc_test.go
Comment on lines +13 to 21
os.Remove("dtc_1.db")

d, err := sql.Open("sqlite3", "file:dtc_1.db?cache=shared&mode=rwc")
require.NoError(t, err)

_, err = d.Exec("DROP TABLE IF EXISTS `dtc_1`")
require.NoError(t, err)

_, err = d.Exec("CREATE TABLE `dtc_1` (`id` int , `email` varchar(50),`created_at` DATETIME, PRIMARY KEY (`id`))")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (testing): Strengthen TestDTCWithDB isolation and cleanup for repeatable runs.

The added DROP TABLE IF EXISTS and file removal help, but the test still relies on a fixed file name (dtc_1.db) and may leave artifacts if it fails partway through. Please ensure cleanup is guaranteed (e.g., via t.Cleanup/defer to remove the file and drop the table) and consider using a unique or temp DB filename per run to avoid test interference and flakiness in CI or repeated local runs.

Suggested implementation:

func TestDTCWithDB(t *testing.T) {

func TestDTCWithDB(t *testing.T) {
	dbDir := t.TempDir()
	dbPath := filepath.Join(dbDir, "dtc_1.db")

	d, err := sql.Open("sqlite3", fmt.Sprintf("file:%s?cache=shared&mode=rwc", dbPath))
	require.NoError(t, err)

	t.Cleanup(func() {
		// Best-effort cleanup to keep runs isolated and avoid DB artifacts.
		_, _ = d.Exec("DROP TABLE IF EXISTS `dtc_1`")
		_ = d.Close()
		_ = os.Remove(dbPath)
	})

To compile and run successfully, you will also need to:

  1. Ensure the import block for this test file includes:
    • fmt
    • path/filepath
      (and os if it is not already imported).
      For example:
    import (
        "database/sql"
        "fmt"
        "os"
        "path/filepath"
        "testing"
    
        "github.com/stretchr/testify/require"
    )
  2. Leave the rest of TestDTCWithDB (the CREATE TABLE, test logic, assertions, etc.) as-is below the shown snippet so it continues to use d as before.

@deepsource-io

deepsource-io Bot commented Dec 1, 2025

Copy link
Copy Markdown

Here's the code health analysis summary for commits ef2c555..46dec1b. View details on DeepSource ↗.

Analysis Summary

AnalyzerStatusSummaryLink
DeepSource Go LogoGo✅ SuccessView Check ↗

💡 If you’re a repository administrator, you can configure the quality gates from the settings.

@codecov

codecov Bot commented Dec 1, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 58.82353% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.58%. Comparing base (ef2c555) to head (46dec1b).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
migrate/migrator.go 58.82% 5 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #54      +/-   ##
==========================================
+ Coverage   75.73%   77.58%   +1.84%     
==========================================
  Files          46       46              
  Lines        2370     1896     -474     
==========================================
- Hits         1795     1471     -324     
+ Misses        456      304     -152     
- Partials      119      121       +2     
Flag Coverage Δ
Unit-Tests 77.58% <58.82%> (+1.84%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@cnlangzi
cnlangzi merged commit d222046 into main Dec 1, 2025
4 checks passed
@cnlangzi
cnlangzi deleted the fix/migrate branch December 1, 2025 07:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant