diff --git a/.changeset/major-remove-d1-backups.md b/.changeset/major-remove-d1-backups.md new file mode 100644 index 000000000000..4dc8eba99ded --- /dev/null +++ b/.changeset/major-remove-d1-backups.md @@ -0,0 +1,11 @@ +--- +"wrangler": major +--- + +Remove `wrangler d1 backups` + +BREAKING CHANGE: This change removes `wrangler d1 backups`, a set of alpha-only commands that would allow folks to interact with backups of their D1 alpha DBs. + +For production D1 DBs, you can restore previous versions of your database with `wrangler d1 time-travel` and export it at any time with `wrangler d1 export`. + +Closes #7470 diff --git a/.changeset/major-remove-d1-deprecated-batch-size.md b/.changeset/major-remove-d1-deprecated-batch-size.md new file mode 100644 index 000000000000..82309e296aa6 --- /dev/null +++ b/.changeset/major-remove-d1-deprecated-batch-size.md @@ -0,0 +1,9 @@ +--- +"wrangler": major +--- + +Remove `--batch-size` as an option for `wrangler d1 execute` and `wrangler d1 migrations apply` + +BREAKING CHANGE: This change removes the deprecated `--batch-size` flag, as it is no longer necessary to decrease the number of queries wrangler sends to D1. + +Closes #7470 diff --git a/.changeset/major-remove-d1-migrations-alpha.md b/.changeset/major-remove-d1-migrations-alpha.md new file mode 100644 index 000000000000..232561f28c07 --- /dev/null +++ b/.changeset/major-remove-d1-migrations-alpha.md @@ -0,0 +1,11 @@ +--- +"wrangler": major +--- + +Remove alpha support from `wrangler d1 migrations apply` + +BREAKING CHANGE: This change removes code that would take a backup of D1 alpha databases before proceeding with applying a migration. + +We can remove this code as alpha DBs have not accepted queries in months. + +Closes #7470 diff --git a/packages/wrangler/src/__tests__/d1/d1.test.ts b/packages/wrangler/src/__tests__/d1/d1.test.ts index 92229f8f8333..9200a9344518 100644 --- a/packages/wrangler/src/__tests__/d1/d1.test.ts +++ b/packages/wrangler/src/__tests__/d1/d1.test.ts @@ -22,7 +22,6 @@ describe("d1", () => { wrangler d1 insights Experimental command. Get information about the queries run on a D1 database. wrangler d1 create Create D1 database wrangler d1 delete Delete D1 database - wrangler d1 backup Interact with D1 backups wrangler d1 execute Execute a command or SQL file wrangler d1 export Export the contents or schema of your database as a .sql file wrangler d1 time-travel Use Time Travel to restore, fork or copy a database at a specific point-in-time @@ -58,7 +57,6 @@ describe("d1", () => { wrangler d1 insights Experimental command. Get information about the queries run on a D1 database. wrangler d1 create Create D1 database wrangler d1 delete Delete D1 database - wrangler d1 backup Interact with D1 backups wrangler d1 execute Execute a command or SQL file wrangler d1 export Export the contents or schema of your database as a .sql file wrangler d1 time-travel Use Time Travel to restore, fork or copy a database at a specific point-in-time diff --git a/packages/wrangler/src/__tests__/d1/migrate.test.ts b/packages/wrangler/src/__tests__/d1/migrate.test.ts index 47c7412494c9..5140ab37d8f6 100644 --- a/packages/wrangler/src/__tests__/d1/migrate.test.ts +++ b/packages/wrangler/src/__tests__/d1/migrate.test.ts @@ -123,6 +123,7 @@ Your database may not be available to serve requests during the migration, conti }); it("multiple accounts: should let the user apply migrations with an account_id in config", async () => { setIsTTY(false); + const std = mockConsoleMethods(); msw.use( http.post( "*/accounts/:accountId/d1/database/:databaseId/query", @@ -154,7 +155,7 @@ Your database may not be available to serve requests during the migration, conti name: "benchmark3-v1", num_tables: 2, uuid: "7b0c1d24-ec57-4179-8663-9b82dafe9277", - version: "alpha", + version: "production", }, success: true, errors: [], @@ -162,15 +163,7 @@ Your database may not be available to serve requests during the migration, conti }, { status: 200 } ); - }), - http.post( - "*/accounts/:accountId/d1/database/:databaseId/backup", - async ({ params }) => { - // All we need to do here is check that the right account ID was provided. - expect(params.accountId).toMatchInlineSnapshot(`"nx01"`); - return HttpResponse.error(); - } - ) + }) ); writeWranglerConfig({ d1_databases: [ @@ -198,12 +191,8 @@ Ok to create /tmp/my-migrations-go-here?`, Your database may not be available to serve requests during the migration, continue?`, result: true, }); - - await expect( - runWrangler("d1 migrations apply db --remote") - ).rejects.toThrowErrorMatchingInlineSnapshot( - `[TypeError: Failed to fetch]` - ); + await runWrangler("d1 migrations apply db --remote"); + expect(std.out).toBe(""); }); }); diff --git a/packages/wrangler/src/d1/backups.ts b/packages/wrangler/src/d1/backups.ts deleted file mode 100644 index 2654875d9547..000000000000 --- a/packages/wrangler/src/d1/backups.ts +++ /dev/null @@ -1,216 +0,0 @@ -import fs from "node:fs/promises"; -import * as path from "path"; -import { fetchResult } from "../cfetch"; -import { performApiFetch } from "../cfetch/internal"; -import { withConfig } from "../config"; -import { logger } from "../logger"; -import { requireAuth } from "../user"; -import { formatBytes, formatTimeAgo } from "./formatTimeAgo"; -import { Name } from "./options"; -import { getDatabaseByNameOrBinding } from "./utils"; -import type { - CommonYargsArgv, - StrictYargsOptionsToInterface, -} from "../yargs-types"; -import type { Backup, Database } from "./types"; -import type { Response } from "undici"; - -export function ListOptions(yargs: CommonYargsArgv) { - return Name(yargs); -} -type ListHandlerOptions = StrictYargsOptionsToInterface; -export const ListHandler = withConfig( - async ({ config, name }): Promise => { - const accountId = await requireAuth(config); - - const db: Database = await getDatabaseByNameOrBinding( - config, - accountId, - name - ); - - const backups: Backup[] = await listBackups(accountId, db.uuid); - logger.table( - backups.map((b) => ({ - created_at: b.created_at, - id: b.id, - num_tables: String(b.num_tables), - size: b.size ?? "", - })) - ); - } -); - -const listBackups = async ( - accountId: string, - uuid: string -): Promise> => { - const json: Backup[] = await fetchResult( - `/accounts/${accountId}/d1/database/${uuid}/backup`, - {} - ); - const results: Record = {}; - - json - // First, convert created_at to a Date - .map((backup) => ({ - ...backup, - created_at: new Date(backup.created_at), - })) - // Then, sort descending based on created_at - .sort((a, b) => +b.created_at - +a.created_at) - // then group_by their human-readable timestamp i.e. "2 days ago" - // (storing only the first of each group) - // and replace the Date version with this new human-readable one - .forEach((backup) => { - const timeAgo = formatTimeAgo(backup.created_at); - if (!results[timeAgo]) { - results[timeAgo] = { - ...backup, - created_at: timeAgo, - size: formatBytes(backup.file_size), - }; - } - }); - - // Take advantage of JS objects' sorting to return the newest backup of a certain age - return Object.values(results); -}; - -export function CreateOptions(yargs: CommonYargsArgv) { - return ListOptions(yargs); -} -type CreateHandlerOptions = StrictYargsOptionsToInterface; - -export const CreateHandler = withConfig( - async ({ config, name }): Promise => { - const accountId = await requireAuth(config); - - const db: Database = await getDatabaseByNameOrBinding( - config, - accountId, - name - ); - - const backup: Backup = await createBackup(accountId, db.uuid); - logger.table( - [backup].map((b) => ({ - created_at: b.created_at, - id: b.id, - num_tables: String(b.num_tables), - size: b.size ?? "", - })) - ); - } -); - -export const createBackup = async ( - accountId: string, - uuid: string -): Promise => { - const backup: Backup = await fetchResult( - `/accounts/${accountId}/d1/database/${uuid}/backup`, - { - method: "POST", - } - ); - return { - ...backup, - size: formatBytes(backup.file_size), - }; -}; - -export function RestoreOptions(yargs: CommonYargsArgv) { - return ListOptions(yargs).positional("backup-id", { - describe: "The Backup ID to restore", - type: "string", - demandOption: true, - }); -} -type RestoreHandlerOptions = StrictYargsOptionsToInterface< - typeof RestoreOptions ->; -export const RestoreHandler = withConfig( - async ({ config, name, backupId }): Promise => { - const accountId = await requireAuth(config); - - const db: Database = await getDatabaseByNameOrBinding( - config, - accountId, - name - ); - - logger.log(`Restoring ${name} from backup ${backupId}....`); - await restoreBackup(accountId, db.uuid, backupId); - logger.log(`Done!`); - } -); - -const restoreBackup = async ( - accountId: string, - uuid: string, - backupId: string -): Promise => { - await fetchResult( - `/accounts/${accountId}/d1/database/${uuid}/backup/${backupId}/restore`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - } - ); -}; - -export function DownloadOptions(yargs: CommonYargsArgv) { - return ListOptions(yargs) - .positional("backup-id", { - describe: "The Backup ID to download", - type: "string", - demandOption: true, - }) - .option("output", { - describe: - "The .sqlite3 file to write to (defaults to '..sqlite3'", - type: "string", - }); -} -type DownloadHandlerOptions = StrictYargsOptionsToInterface< - typeof DownloadOptions ->; -export const DownloadHandler = withConfig( - async ({ name, backupId, output, config }): Promise => { - const accountId = await requireAuth(config); - - const db: Database = await getDatabaseByNameOrBinding( - config, - accountId, - name - ); - const filename = - output || path.resolve(`${name}.${backupId.slice(0, 8)}.sqlite3`); - - logger.log(`🌀 Downloading backup ${backupId} from '${name}'`); - const response = await getBackupResponse(accountId, db.uuid, backupId); - if (!response.ok) { - throw new Error( - `Failed to download backup ${backupId} from '${name}' - got ${response.status} from the API` - ); - } - logger.log(`🌀 Saving to ${filename}`); - // TODO: stream this once we upgrade to Node18 and can use Writable.fromWeb - const buffer = await response.arrayBuffer(); - await fs.writeFile(filename, new Buffer(buffer)); - logger.log(`🌀 Done!`); - } -); - -const getBackupResponse = async ( - accountId: string, - uuid: string, - backupId: string -): Promise => { - return await performApiFetch( - `/accounts/${accountId}/d1/database/${uuid}/backup/${backupId}/download` - ); -}; diff --git a/packages/wrangler/src/d1/execute.ts b/packages/wrangler/src/d1/execute.ts index bea1bd4fe49d..a9a1e7ee1c49 100644 --- a/packages/wrangler/src/d1/execute.ts +++ b/packages/wrangler/src/d1/execute.ts @@ -81,12 +81,6 @@ export function Options(yargs: CommonYargsArgv) { describe: "Execute commands/files against a preview D1 DB", type: "boolean", default: false, - }) - .option("batch-size", { - describe: "Number of queries to send in a single batch", - type: "number", - deprecated: true, - hidden: true, }); } diff --git a/packages/wrangler/src/d1/index.ts b/packages/wrangler/src/d1/index.ts index 43b22f169f87..17c6df0a9ff2 100644 --- a/packages/wrangler/src/d1/index.ts +++ b/packages/wrangler/src/d1/index.ts @@ -1,4 +1,3 @@ -import * as Backups from "./backups"; import * as Create from "./create"; import * as Delete from "./delete"; import * as Execute from "./execute"; @@ -38,34 +37,6 @@ export function d1(yargs: CommonYargsArgv) { Delete.Options, Delete.Handler ) - .command("backup", "Interact with D1 backups", (backupArgs) => - backupArgs - .demandCommand() - .command( - "list ", - "List your D1 backups", - Backups.ListOptions, - Backups.ListHandler - ) - .command( - "create ", - "Create a new D1 backup", - Backups.CreateOptions, - Backups.CreateHandler - ) - .command( - "restore ", - "Restore a DB backup", - Backups.RestoreOptions, - Backups.RestoreHandler - ) - .command( - "download ", - "Download a DB backup", - Backups.DownloadOptions, - Backups.DownloadHandler - ) - ) // .command( // "console ", // "Open a Console on a D1 database", diff --git a/packages/wrangler/src/d1/migrations/apply.ts b/packages/wrangler/src/d1/migrations/apply.ts index f1676409452b..be3148faaca9 100644 --- a/packages/wrangler/src/d1/migrations/apply.ts +++ b/packages/wrangler/src/d1/migrations/apply.ts @@ -1,4 +1,3 @@ -import assert from "node:assert"; import fs from "node:fs"; import path from "path"; import { printWranglerBanner } from "../.."; @@ -7,11 +6,9 @@ import { confirm } from "../../dialogs"; import { UserError } from "../../errors"; import { isNonInteractiveOrCI } from "../../is-interactive"; import { logger } from "../../logger"; -import { requireAuth } from "../../user"; -import { createBackup } from "../backups"; import { DEFAULT_MIGRATION_PATH, DEFAULT_MIGRATION_TABLE } from "../constants"; import { executeSql } from "../execute"; -import { getDatabaseInfoFromConfig, getDatabaseInfoFromId } from "../utils"; +import { getDatabaseInfoFromConfig } from "../utils"; import { getMigrationsPath, getUnappliedMigrations, @@ -25,11 +22,7 @@ import type { } from "../../yargs-types"; export function ApplyOptions(yargs: CommonYargsArgv) { - return MigrationOptions(yargs).option("batch-size", { - describe: "Number of queries to send in a single batch", - type: "number", - deprecated: true, - }); + return MigrationOptions(yargs); } type ApplyHandlerOptions = StrictYargsOptionsToInterface; @@ -123,20 +116,6 @@ Your database may not be available to serve requests during the migration, conti return; } - // don't backup prod db when applying migrations locally, in preview, or when using the experimental backend - if (!(local || preview)) { - assert( - databaseInfo, - "In non-local mode `databaseInfo` should be defined." - ); - const accountId = await requireAuth(config); - const dbInfo = await getDatabaseInfoFromId(accountId, databaseInfo?.uuid); - if (dbInfo.version === "alpha") { - logger.log("🕒 Creating backup..."); - await createBackup(accountId, databaseInfo.uuid); - } - } - for (const migration of unappliedMigrations) { let query = fs.readFileSync( `${migrationsPath}/${migration.name}`,