Skip to content

[wrangler/d1] Support migrations in a directory (multi-file migrations)#12703

Draft
Ehbraheem wants to merge 4 commits intocloudflare:mainfrom
Ehbraheem:core/d1-directory-based-migrations
Draft

[wrangler/d1] Support migrations in a directory (multi-file migrations)#12703
Ehbraheem wants to merge 4 commits intocloudflare:mainfrom
Ehbraheem:core/d1-directory-based-migrations

Conversation

@Ehbraheem
Copy link
Copy Markdown

@Ehbraheem Ehbraheem commented Feb 27, 2026

Fixes #11947.

Summary

Allow a migration to be either:

  • a top-level .sql file (unchanged), or
  • a directory whose name is the migration name and which contains one or more .sql files (executed in deterministic order).

  • Tests
    • Tests included
    • Tests not necessary because:
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because: internal behavior change; README/docs follow-up can be added separately.
  • Wrangler V3 Backport
    • Wrangler PR:
    • Not necessary because: change is contained to D1 migration logic and tests in this repo.

Open with Devin

@Ehbraheem Ehbraheem requested review from a team as code owners February 27, 2026 16:35
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Feb 27, 2026

🦋 Changeset detected

Latest commit: b3405a9

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View 5 additional findings in Devin Review.

Open in Devin Review

Comment on lines +247 to +250
const files = fs.readdirSync(fullPath);
return files
.filter((f) => f.endsWith(".sql"))
.map((f) => path.join(fullPath, f));
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.

🔴 Missing sort on SQL files within a migration directory causes non-deterministic execution order

The resolveMigrationFiles function reads .sql files from a migration directory using fs.readdirSync() but does not sort the results before returning them. Node.js's readdirSync does not guarantee any particular ordering — it depends on the underlying filesystem. The PR description explicitly promises files are "executed in deterministic order", and the test uses files named 01_schema.sql and 02_seed.sql implying order matters (e.g., schema must be created before data is seeded).

Root Cause and Impact

At packages/wrangler/src/d1/migrations/apply.ts:247-250, the files are read from the directory and filtered but never sorted:

const files = fs.readdirSync(fullPath);
return files
    .filter((f) => f.endsWith(".sql"))
    .map((f) => path.join(fullPath, f));

On many Linux filesystems (e.g., ext4 with dir_index), small directories may appear sorted, which is why tests pass. However, on other filesystems (XFS, Btrfs, or ext4 with hash-tree directories containing many entries), the order can differ. This means a migration directory containing 01_schema.sql (CREATE TABLE) and 02_seed.sql (INSERT INTO that table) could execute the INSERT before the CREATE, causing the migration to fail.

Impact: Migration directories with order-dependent SQL files may fail unpredictably depending on the filesystem, making this a latent production bug that's hard to reproduce in testing.

Suggested change
const files = fs.readdirSync(fullPath);
return files
.filter((f) => f.endsWith(".sql"))
.map((f) => path.join(fullPath, f));
const files = fs.readdirSync(fullPath);
return files
.filter((f) => f.endsWith(".sql"))
.sort()
.map((f) => path.join(fullPath, f));
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@petebacondarwin
Copy link
Copy Markdown
Contributor

Please could you rebase and resolve the conflicts.

@petebacondarwin petebacondarwin marked this pull request as draft April 15, 2026 06:29
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.

d1 migrations apply: Support for nested migrations

2 participants