Skip to content

Commit 3362163

Browse files
committed
feat: Add table-level compaction commands for R2 Data Catalog
1 parent e483b78 commit 3362163

File tree

5 files changed

+393
-0
lines changed

5 files changed

+393
-0
lines changed

.changeset/fine-pans-sit.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": minor
3+
---
4+
5+
feat: [R2 Data Catalog] Add table-level compaction commands

packages/wrangler/src/__tests__/r2.test.ts

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,7 @@ describe("r2", () => {
10111011
wrangler r2 bucket catalog disable <bucket> Disable the data catalog for an R2 bucket [open-beta]
10121012
wrangler r2 bucket catalog get <bucket> Get the status of the data catalog for an R2 bucket [open-beta]
10131013
wrangler r2 bucket catalog compaction Control settings for automatic file compaction maintenance jobs for your R2 data catalog [open-beta]
1014+
wrangler r2 bucket catalog table Manage table-level maintenance for your R2 data catalog [open-beta]
10141015
10151016
GLOBAL FLAGS
10161017
-c, --config Path to Wrangler configuration file [string]
@@ -1505,6 +1506,194 @@ describe("r2", () => {
15051506
});
15061507
});
15071508
});
1509+
1510+
describe("table", () => {
1511+
describe("compaction", () => {
1512+
const { setIsTTY } = useMockIsTTY();
1513+
1514+
it("should show the correct help when an invalid command is passed", async () => {
1515+
await expect(() =>
1516+
runWrangler("r2 bucket catalog table compaction foo")
1517+
).rejects.toThrowErrorMatchingInlineSnapshot(
1518+
`[Error: Unknown argument: foo]`
1519+
);
1520+
expect(std.err).toMatchInlineSnapshot(`
1521+
"X [ERROR] Unknown argument: foo
1522+
1523+
"
1524+
`);
1525+
expect(std.out).toMatchInlineSnapshot(`
1526+
"
1527+
wrangler r2 bucket catalog table compaction
1528+
1529+
Control table-level automatic file compaction for your R2 data catalog [open-beta]
1530+
1531+
COMMANDS
1532+
wrangler r2 bucket catalog table compaction enable <bucket> <namespace> <table> Enable automatic file compaction for a specific R2 data catalog table [open-beta]
1533+
wrangler r2 bucket catalog table compaction disable <bucket> <namespace> <table> Disable automatic file compaction for a specific R2 data catalog table [open-beta]
1534+
1535+
GLOBAL FLAGS
1536+
-c, --config Path to Wrangler configuration file [string]
1537+
--cwd Run as if Wrangler was started in the specified directory instead of the current working directory [string]
1538+
-e, --env Environment to use for operations, and for selecting .env and .dev.vars files [string]
1539+
--env-file Path to an .env file to load - can be specified multiple times - values from earlier files are overridden by values in later files [array]
1540+
-h, --help Show help [boolean]
1541+
-v, --version Show version number [boolean]"
1542+
`);
1543+
});
1544+
1545+
describe("enable", () => {
1546+
it("should enable table compaction with default target size", async () => {
1547+
msw.use(
1548+
http.post(
1549+
"*/accounts/some-account-id/r2-catalog/testBucket/namespaces/testNamespace/tables/testTable/maintenance-configs",
1550+
async ({ request }) => {
1551+
const body = await request.json();
1552+
expect(request.method).toEqual("POST");
1553+
expect(body).toEqual({
1554+
compaction: {
1555+
state: "enabled",
1556+
},
1557+
});
1558+
return HttpResponse.json(
1559+
createFetchResult({ success: true }, true)
1560+
);
1561+
},
1562+
{ once: true }
1563+
)
1564+
);
1565+
await runWrangler(
1566+
"r2 bucket catalog table compaction enable testBucket testNamespace testTable"
1567+
);
1568+
expect(std.out).toMatchInlineSnapshot(
1569+
`
1570+
"
1571+
⛅️ wrangler x.x.x
1572+
──────────────────
1573+
✨ Successfully enabled file compaction for table 'testNamespace.testTable' in bucket 'testBucket'."
1574+
`
1575+
);
1576+
});
1577+
1578+
it("should enable table compaction with custom target size", async () => {
1579+
msw.use(
1580+
http.post(
1581+
"*/accounts/some-account-id/r2-catalog/testBucket/namespaces/testNamespace/tables/testTable/maintenance-configs",
1582+
async ({ request }) => {
1583+
const body = await request.json();
1584+
expect(request.method).toEqual("POST");
1585+
expect(body).toEqual({
1586+
compaction: {
1587+
state: "enabled",
1588+
target_size_mb: 256,
1589+
},
1590+
});
1591+
return HttpResponse.json(
1592+
createFetchResult({ success: true }, true)
1593+
);
1594+
},
1595+
{ once: true }
1596+
)
1597+
);
1598+
await runWrangler(
1599+
"r2 bucket catalog table compaction enable testBucket testNamespace testTable --target-size 256"
1600+
);
1601+
expect(std.out).toMatchInlineSnapshot(
1602+
`
1603+
"
1604+
⛅️ wrangler x.x.x
1605+
──────────────────
1606+
✨ Successfully enabled file compaction for table 'testNamespace.testTable' in bucket 'testBucket'."
1607+
`
1608+
);
1609+
});
1610+
1611+
it("should error if no bucket name is given", async () => {
1612+
await expect(
1613+
runWrangler("r2 bucket catalog table compaction enable")
1614+
).rejects.toThrowErrorMatchingInlineSnapshot(
1615+
`[Error: Not enough non-option arguments: got 0, need at least 3]`
1616+
);
1617+
});
1618+
1619+
it("should error if namespace is missing", async () => {
1620+
await expect(
1621+
runWrangler(
1622+
"r2 bucket catalog table compaction enable testBucket"
1623+
)
1624+
).rejects.toThrowErrorMatchingInlineSnapshot(
1625+
`[Error: Not enough non-option arguments: got 1, need at least 3]`
1626+
);
1627+
});
1628+
1629+
it("should error if table name is missing", async () => {
1630+
await expect(
1631+
runWrangler(
1632+
"r2 bucket catalog table compaction enable testBucket testNamespace"
1633+
)
1634+
).rejects.toThrowErrorMatchingInlineSnapshot(
1635+
`[Error: Not enough non-option arguments: got 2, need at least 3]`
1636+
);
1637+
});
1638+
});
1639+
1640+
describe("disable", () => {
1641+
it("should disable table compaction when confirmation is accepted", async () => {
1642+
setIsTTY(true);
1643+
mockConfirm({
1644+
text: "Are you sure you want to disable file compaction for table 'testNamespace.testTable' in bucket 'testBucket'?",
1645+
result: true,
1646+
});
1647+
msw.use(
1648+
http.post(
1649+
"*/accounts/some-account-id/r2-catalog/testBucket/namespaces/testNamespace/tables/testTable/maintenance-configs",
1650+
async ({ request }) => {
1651+
const body = await request.json();
1652+
expect(request.method).toEqual("POST");
1653+
expect(body).toEqual({
1654+
compaction: {
1655+
state: "disabled",
1656+
},
1657+
});
1658+
return HttpResponse.json(
1659+
createFetchResult({ success: true }, true)
1660+
);
1661+
},
1662+
{ once: true }
1663+
)
1664+
);
1665+
await runWrangler(
1666+
"r2 bucket catalog table compaction disable testBucket testNamespace testTable"
1667+
);
1668+
expect(std.out).toMatchInlineSnapshot(
1669+
`
1670+
"
1671+
⛅️ wrangler x.x.x
1672+
──────────────────
1673+
Successfully disabled file compaction for table 'testNamespace.testTable' in bucket 'testBucket'."
1674+
`
1675+
);
1676+
});
1677+
1678+
it("should cancel disable when confirmation is rejected", async () => {
1679+
setIsTTY(true);
1680+
mockConfirm({
1681+
text: "Are you sure you want to disable file compaction for table 'testNamespace.testTable' in bucket 'testBucket'?",
1682+
result: false,
1683+
});
1684+
await runWrangler(
1685+
"r2 bucket catalog table compaction disable testBucket testNamespace testTable"
1686+
);
1687+
expect(std.out).toMatchInlineSnapshot(`
1688+
"
1689+
⛅️ wrangler x.x.x
1690+
──────────────────
1691+
Disable cancelled."
1692+
`);
1693+
});
1694+
});
1695+
});
1696+
});
15081697
});
15091698

15101699
describe("notification", () => {

packages/wrangler/src/index.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,10 @@ import {
210210
r2BucketCatalogEnableCommand,
211211
r2BucketCatalogGetCommand,
212212
r2BucketCatalogNamespace,
213+
r2BucketCatalogTableCompactionDisableCommand,
214+
r2BucketCatalogTableCompactionEnableCommand,
215+
r2BucketCatalogTableCompactionNamespace,
216+
r2BucketCatalogTableNamespace,
213217
} from "./r2/catalog";
214218
import {
215219
r2BucketCORSDeleteCommand,
@@ -877,6 +881,22 @@ export function createCLIParser(argv: string[]) {
877881
command: "wrangler r2 bucket catalog compaction disable",
878882
definition: r2BucketCatalogCompactionDisableCommand,
879883
},
884+
{
885+
command: "wrangler r2 bucket catalog table",
886+
definition: r2BucketCatalogTableNamespace,
887+
},
888+
{
889+
command: "wrangler r2 bucket catalog table compaction",
890+
definition: r2BucketCatalogTableCompactionNamespace,
891+
},
892+
{
893+
command: "wrangler r2 bucket catalog table compaction enable",
894+
definition: r2BucketCatalogTableCompactionEnableCommand,
895+
},
896+
{
897+
command: "wrangler r2 bucket catalog table compaction disable",
898+
definition: r2BucketCatalogTableCompactionDisableCommand,
899+
},
880900
{
881901
command: "wrangler r2 bucket notification",
882902
definition: r2BucketNotificationNamespace,

packages/wrangler/src/r2/catalog.ts

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import formatLabelledValues from "../utils/render-labelled-values";
88
import {
99
disableR2Catalog,
1010
disableR2CatalogCompaction,
11+
disableR2CatalogTableCompaction,
1112
enableR2Catalog,
1213
enableR2CatalogCompaction,
14+
enableR2CatalogTableCompaction,
1315
getR2Catalog,
1416
upsertR2DataCatalogCredential,
1517
} from "./helpers";
@@ -250,3 +252,119 @@ export const r2BucketCatalogCompactionDisableCommand = createCommand({
250252
);
251253
},
252254
});
255+
256+
export const r2BucketCatalogTableNamespace = createNamespace({
257+
metadata: {
258+
description: "Manage table-level maintenance for your R2 data catalog",
259+
status: "open-beta",
260+
owner: "Product: R2 Data Catalog",
261+
},
262+
});
263+
264+
export const r2BucketCatalogTableCompactionNamespace = createNamespace({
265+
metadata: {
266+
description:
267+
"Control table-level automatic file compaction for your R2 data catalog",
268+
status: "open-beta",
269+
owner: "Product: R2 Data Catalog",
270+
},
271+
});
272+
273+
export const r2BucketCatalogTableCompactionEnableCommand = createCommand({
274+
metadata: {
275+
description:
276+
"Enable automatic file compaction for a specific R2 data catalog table",
277+
status: "open-beta",
278+
owner: "Product: R2 Data Catalog",
279+
},
280+
positionalArgs: ["bucket", "namespace", "table"],
281+
args: {
282+
bucket: {
283+
describe: "The name of the bucket which contains the catalog",
284+
type: "string",
285+
demandOption: true,
286+
},
287+
namespace: {
288+
describe: "The namespace containing the table",
289+
type: "string",
290+
demandOption: true,
291+
},
292+
table: {
293+
describe: "The name of the table",
294+
type: "string",
295+
demandOption: true,
296+
},
297+
"target-size": {
298+
describe:
299+
"The target size for compacted files in MB (allowed values: 64, 128, 256, 512)",
300+
type: "number",
301+
demandOption: false,
302+
},
303+
},
304+
async handler(args, { config }) {
305+
const accountId = await requireAuth(config);
306+
307+
await enableR2CatalogTableCompaction(
308+
config,
309+
accountId,
310+
args.bucket,
311+
args.namespace,
312+
args.table,
313+
args.targetSize
314+
);
315+
316+
logger.log(
317+
`✨ Successfully enabled file compaction for table '${args.namespace}.${args.table}' in bucket '${args.bucket}'.`
318+
);
319+
},
320+
});
321+
322+
export const r2BucketCatalogTableCompactionDisableCommand = createCommand({
323+
metadata: {
324+
description:
325+
"Disable automatic file compaction for a specific R2 data catalog table",
326+
status: "open-beta",
327+
owner: "Product: R2 Data Catalog",
328+
},
329+
positionalArgs: ["bucket", "namespace", "table"],
330+
args: {
331+
bucket: {
332+
describe: "The name of the bucket which contains the catalog",
333+
type: "string",
334+
demandOption: true,
335+
},
336+
namespace: {
337+
describe: "The namespace containing the table",
338+
type: "string",
339+
demandOption: true,
340+
},
341+
table: {
342+
describe: "The name of the table",
343+
type: "string",
344+
demandOption: true,
345+
},
346+
},
347+
async handler(args, { config }) {
348+
const accountId = await requireAuth(config);
349+
350+
const confirmedDisable = await confirm(
351+
`Are you sure you want to disable file compaction for table '${args.namespace}.${args.table}' in bucket '${args.bucket}'?`
352+
);
353+
if (!confirmedDisable) {
354+
logger.log("Disable cancelled.");
355+
return;
356+
}
357+
358+
await disableR2CatalogTableCompaction(
359+
config,
360+
accountId,
361+
args.bucket,
362+
args.namespace,
363+
args.table
364+
);
365+
366+
logger.log(
367+
`Successfully disabled file compaction for table '${args.namespace}.${args.table}' in bucket '${args.bucket}'.`
368+
);
369+
},
370+
});

0 commit comments

Comments
 (0)