Skip to content

Commit b6ed724

Browse files
garvit-guptapetebacondarwin
authored andcommitted
feat: Add table-level compaction commands for R2 Data Catalog (#10881)
1 parent b22bda2 commit b6ed724

File tree

6 files changed

+476
-43
lines changed

6 files changed

+476
-43
lines changed

.changeset/fine-pans-sit.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"wrangler": minor
3+
---
4+
5+
Add table-level compaction commands for R2 Data Catalog:
6+
7+
- `wrangler r2 bucket catalog compaction enable <bucket> [namespace] [table]`
8+
- `wrangler r2 bucket catalog compaction disable <bucket> [namespace] [table]`
9+
10+
This allows you to enable and disable automatic file compaction for a specific R2 data catalog table.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: Major release validation
2+
3+
on:
4+
merge_group:
5+
pull_request:
6+
7+
permissions:
8+
contents: read
9+
10+
jobs:
11+
check:
12+
concurrency:
13+
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.os }}-major-release-validation
14+
cancel-in-progress: true
15+
16+
name: "Major release validation"
17+
runs-on: ubuntu-24.04-arm
18+
steps:
19+
- name: Checkout Repo
20+
uses: actions/checkout@v4
21+
with:
22+
fetch-depth: 100
23+
24+
# Find all the changesets that were modified or added in this pull request
25+
- uses: dorny/paths-filter@v3
26+
id: filters
27+
with:
28+
list-files: "json"
29+
predicate-quantifier: "every"
30+
filters: |
31+
updated_changesets:
32+
- added|modified: '.changeset/*.md'
33+
- added|modified: '!.changeset/README.md'
34+
35+
- name: Install Dependencies
36+
if: steps.filters.outputs.updated_changesets == 'true'
37+
uses: ./.github/actions/install-dependencies
38+
39+
# Validate that the pull request is marked with the appropriate label if any of the modified or added changesets have major releases
40+
- name: Validate major changes
41+
if: steps.filters.outputs.updated_changesets == 'true'
42+
run: node -r esbuild-register tools/deployments/validate-major-changes.ts
43+
with:
44+
GITHUB_TOKEN: ${{ github.token }}
45+
CHANGES: ${{ steps.filters.outputs.updated_changesets_files }}
46+
LABEL: "breaking-change"

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

Lines changed: 159 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,8 +1312,8 @@ describe("r2", () => {
13121312
Control settings for automatic file compaction maintenance jobs for your R2 data catalog [open-beta]
13131313
13141314
COMMANDS
1315-
wrangler r2 bucket catalog compaction enable <bucket> Enable automatic file compaction for your R2 data catalog [open-beta]
1316-
wrangler r2 bucket catalog compaction disable <bucket> Disable automatic file compaction for your R2 data catalog [open-beta]
1315+
wrangler r2 bucket catalog compaction enable <bucket> [namespace] [table] Enable automatic file compaction for your R2 data catalog or a specific table [open-beta]
1316+
wrangler r2 bucket catalog compaction disable <bucket> [namespace] [table] Disable automatic file compaction for your R2 data catalog or a specific table [open-beta]
13171317
13181318
GLOBAL FLAGS
13191319
-c, --config Path to Wrangler configuration file [string]
@@ -1368,7 +1368,10 @@ describe("r2", () => {
13681368
"
13691369
⛅️ wrangler x.x.x
13701370
──────────────────
1371-
✨ Successfully enabled file compaction for the data catalog for bucket 'testBucket'."
1371+
✨ Successfully enabled file compaction for the data catalog for bucket 'testBucket'.
1372+
1373+
Compaction will automatically combine small files into larger ones to improve query performance.
1374+
For more details, refer to: https://developers.cloudflare.com/r2/data-catalog/about-compaction/"
13721375
`
13731376
);
13741377
});
@@ -1381,12 +1384,14 @@ describe("r2", () => {
13811384
);
13821385
expect(std.out).toMatchInlineSnapshot(`
13831386
"
1384-
wrangler r2 bucket catalog compaction enable <bucket>
1387+
wrangler r2 bucket catalog compaction enable <bucket> [namespace] [table]
13851388
1386-
Enable automatic file compaction for your R2 data catalog [open-beta]
1389+
Enable automatic file compaction for your R2 data catalog or a specific table [open-beta]
13871390
13881391
POSITIONALS
1389-
bucket The name of the bucket which contains the catalog [string] [required]
1392+
bucket The name of the bucket which contains the catalog [string] [required]
1393+
namespace The namespace containing the table (optional, for table-level compaction) [string]
1394+
table The name of the table (optional, for table-level compaction) [string]
13901395
13911396
GLOBAL FLAGS
13921397
-c, --config Path to Wrangler configuration file [string]
@@ -1398,7 +1403,7 @@ describe("r2", () => {
13981403
13991404
OPTIONS
14001405
--target-size The target size for compacted files in MB (allowed values: 64, 128, 256, 512) [number] [default: 128]
1401-
--token A cloudflare api token with access to R2 and R2 Data Catalog which will be used to read/write files for compaction. [string] [required]"
1406+
--token A cloudflare api token with access to R2 and R2 Data Catalog (required for catalog-level compaction settings only) [string]"
14021407
`);
14031408
expect(std.err).toMatchInlineSnapshot(`
14041409
"X [ERROR] Not enough non-option arguments: got 0, need at least 1
@@ -1407,13 +1412,99 @@ describe("r2", () => {
14071412
`);
14081413
});
14091414

1410-
it("should error if --token is not provided", async () => {
1415+
it("should error if --token is not provided for catalog-level", async () => {
14111416
await expect(
14121417
runWrangler(
14131418
"r2 bucket catalog compaction enable testBucket --target-size 512"
14141419
)
14151420
).rejects.toThrowErrorMatchingInlineSnapshot(
1416-
`[Error: Missing required argument: token]`
1421+
`[Error: Token is required for catalog-level compaction. Use --token flag to provide a Cloudflare API token.]`
1422+
);
1423+
});
1424+
1425+
it("should enable table compaction without token", async () => {
1426+
msw.use(
1427+
http.post(
1428+
"*/accounts/some-account-id/r2-catalog/testBucket/namespaces/testNamespace/tables/testTable/maintenance-configs",
1429+
async ({ request }) => {
1430+
const body = await request.json();
1431+
expect(request.method).toEqual("POST");
1432+
expect(body).toEqual({
1433+
compaction: {
1434+
state: "enabled",
1435+
},
1436+
});
1437+
return HttpResponse.json(
1438+
createFetchResult({ success: true }, true)
1439+
);
1440+
},
1441+
{ once: true }
1442+
)
1443+
);
1444+
await runWrangler(
1445+
"r2 bucket catalog compaction enable testBucket testNamespace testTable"
1446+
);
1447+
expect(std.out).toMatchInlineSnapshot(
1448+
`
1449+
"
1450+
⛅️ wrangler x.x.x
1451+
──────────────────
1452+
✨ Successfully enabled file compaction for table 'testNamespace.testTable' in bucket 'testBucket'."
1453+
`
1454+
);
1455+
});
1456+
1457+
it("should enable table compaction with custom target size", async () => {
1458+
msw.use(
1459+
http.post(
1460+
"*/accounts/some-account-id/r2-catalog/testBucket/namespaces/testNamespace/tables/testTable/maintenance-configs",
1461+
async ({ request }) => {
1462+
const body = await request.json();
1463+
expect(request.method).toEqual("POST");
1464+
expect(body).toEqual({
1465+
compaction: {
1466+
state: "enabled",
1467+
target_size_mb: 256,
1468+
},
1469+
});
1470+
return HttpResponse.json(
1471+
createFetchResult({ success: true }, true)
1472+
);
1473+
},
1474+
{ once: true }
1475+
)
1476+
);
1477+
await runWrangler(
1478+
"r2 bucket catalog compaction enable testBucket testNamespace testTable --target-size 256"
1479+
);
1480+
expect(std.out).toMatchInlineSnapshot(
1481+
`
1482+
"
1483+
⛅️ wrangler x.x.x
1484+
──────────────────
1485+
✨ Successfully enabled file compaction for table 'testNamespace.testTable' in bucket 'testBucket'."
1486+
`
1487+
);
1488+
});
1489+
1490+
it("should error if only namespace is provided", async () => {
1491+
await expect(
1492+
runWrangler(
1493+
"r2 bucket catalog compaction enable testBucket testNamespace"
1494+
)
1495+
).rejects.toThrowErrorMatchingInlineSnapshot(
1496+
`[Error: Table name is required when namespace is specified]`
1497+
);
1498+
});
1499+
1500+
it("should error if only table is provided", async () => {
1501+
// This test ensures that if table is passed as namespace position, it errors properly
1502+
await expect(
1503+
runWrangler(
1504+
'r2 bucket catalog compaction enable testBucket "" testTable'
1505+
)
1506+
).rejects.toThrowErrorMatchingInlineSnapshot(
1507+
`[Error: Namespace is required when table is specified]`
14171508
);
14181509
});
14191510
});
@@ -1429,12 +1520,14 @@ describe("r2", () => {
14291520
);
14301521
expect(std.out).toMatchInlineSnapshot(`
14311522
"
1432-
wrangler r2 bucket catalog compaction disable <bucket>
1523+
wrangler r2 bucket catalog compaction disable <bucket> [namespace] [table]
14331524
1434-
Disable automatic file compaction for your R2 data catalog [open-beta]
1525+
Disable automatic file compaction for your R2 data catalog or a specific table [open-beta]
14351526
14361527
POSITIONALS
1437-
bucket The name of the bucket which contains the catalog [string] [required]
1528+
bucket The name of the bucket which contains the catalog [string] [required]
1529+
namespace The namespace containing the table (optional, for table-level compaction) [string]
1530+
table The name of the table (optional, for table-level compaction) [string]
14381531
14391532
GLOBAL FLAGS
14401533
-c, --config Path to Wrangler configuration file [string]
@@ -1503,6 +1596,60 @@ describe("r2", () => {
15031596
Disable cancelled."
15041597
`);
15051598
});
1599+
1600+
it("should disable table compaction when confirmed", async () => {
1601+
setIsTTY(true);
1602+
mockConfirm({
1603+
text: "Are you sure you want to disable file compaction for table 'testNamespace.testTable' in bucket 'testBucket'?",
1604+
result: true,
1605+
});
1606+
msw.use(
1607+
http.post(
1608+
"*/accounts/some-account-id/r2-catalog/testBucket/namespaces/testNamespace/tables/testTable/maintenance-configs",
1609+
async ({ request }) => {
1610+
const body = await request.json();
1611+
expect(request.method).toEqual("POST");
1612+
expect(body).toEqual({
1613+
compaction: {
1614+
state: "disabled",
1615+
},
1616+
});
1617+
return HttpResponse.json(
1618+
createFetchResult({ success: true }, true)
1619+
);
1620+
},
1621+
{ once: true }
1622+
)
1623+
);
1624+
await runWrangler(
1625+
"r2 bucket catalog compaction disable testBucket testNamespace testTable"
1626+
);
1627+
expect(std.out).toMatchInlineSnapshot(
1628+
`
1629+
"
1630+
⛅️ wrangler x.x.x
1631+
──────────────────
1632+
Successfully disabled file compaction for table 'testNamespace.testTable' in bucket 'testBucket'."
1633+
`
1634+
);
1635+
});
1636+
1637+
it("should cancel table compaction disable when rejected", async () => {
1638+
setIsTTY(true);
1639+
mockConfirm({
1640+
text: "Are you sure you want to disable file compaction for table 'testNamespace.testTable' in bucket 'testBucket'?",
1641+
result: false,
1642+
});
1643+
await runWrangler(
1644+
"r2 bucket catalog compaction disable testBucket testNamespace testTable"
1645+
);
1646+
expect(std.out).toMatchInlineSnapshot(`
1647+
"
1648+
⛅️ wrangler x.x.x
1649+
──────────────────
1650+
Disable cancelled."
1651+
`);
1652+
});
15061653
});
15071654
});
15081655
});

0 commit comments

Comments
 (0)