Skip to content

Commit ea7f6b1

Browse files
authored
Remove old GC table name format (#18981)
Signed-off-by: Shlomi Noach <[email protected]>
1 parent 1f49210 commit ea7f6b1

File tree

15 files changed

+140
-312
lines changed

15 files changed

+140
-312
lines changed

doc/design-docs/SafeLazyDropTables.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ We wish to manage `DROP` requests. Most managed `DROP` requests will _wait_ befo
2121

2222
This is done by first issuing a `RENAME TABLE my_table TO something_else`. To the app, it seems like the table is gone; but the user may easily restore it by running the revert query: `RENAME TABLE something_else TO my_table`.
2323

24-
That `something_else` name can be e.g. `_vt_HOLD_2201058f_f266_11ea_bab4_0242c0a8b007_20200910113042`.
24+
That `something_else` name can be e.g. `_vt_hld_2201058f_f266_11ea_bab4_0242c0a8b007_20200910113042_`.
2525

2626
At some point we decide that we can destroy the data. The "hold" period can either be determined by vitess or explicitly by the user. e.g. On a successful schema migration completion, Vitess can choose to purge the "old" table right away.
27-
At that stage we rename the table to e.g. `_vt_PURGE_63b5db0c_f25c_11ea_bab4_0242c0a8b007_20200911070228`.
27+
At that stage we rename the table to e.g. `_vt_prg_63b5db0c_f25c_11ea_bab4_0242c0a8b007_20200911070228_`.
2828
A table by that name is eligible to have its data purged.
2929

3030
By experience (see `gh-ost` issue above), a safe method to purge data is to slowly remove rows, until the table is empty. Note:
@@ -35,9 +35,9 @@ By experience (see `gh-ost` issue above), a safe method to purge data is to slow
3535

3636
It's important to note that the `DELETE` statement actually causes table pages to _load into the buffer pool_, which works against our objective.
3737

38-
Once all rows are purged from a table, we rename it again to e.g. `_vt_DROP_8a797518_f25c_11ea_bab4_0242c0a8b007_20210211234156`. At this time we point out that `20200911234156` is actually a readable timestamp, and stands for `2021-02-11 23:41:56`. That timestamp can tell us when the table was last renamed.
38+
Once all rows are purged from a table, we rename it again to e.g. `_vt_drp_8a797518_f25c_11ea_bab4_0242c0a8b007_20210211234156_`. At this time we point out that `20200911234156` is actually a readable timestamp, and stands for `2021-02-11 23:41:56`. That timestamp can tell us when the table was last renamed.
3939

40-
Vitess can then run an actual `DROP TABLE` for `_vt_DROP_...` tables whose timestamp is older than, say, 2 days. As mentioned above, purging the table actually caused the table to load onto the buffer pool, and we need to wait for it to naturally get evicted, before dropping it.
40+
Vitess can then run an actual `DROP TABLE` for `_vt_drp_...` tables whose timestamp is older than, say, 2 days. As mentioned above, purging the table actually caused the table to load onto the buffer pool, and we need to wait for it to naturally get evicted, before dropping it.
4141

4242
## Suggested implementation: table lifecycle aka table garbage collection
4343

@@ -69,11 +69,11 @@ The way to support the above is by introducing user-defined states. The general
6969
Vitess will transition the table through these states, _in order_. But it will also support skipping some states. Let's first explain the meaning of the states:
7070

7171
- Real table (alive): Table is in use in production.
72-
- `HOLD`: Table is renamed to something like `_vt_HOLD_6ace8bcef73211ea87e9f875a4d24e90_20200915120410`. Vitess will not make changes to the table, will not drop data. The table is put away for safe keeping for X hours/days. If it turns out the app still needs the table, the user can `RENAME` is back to its original name, taking it out of this game.
73-
- `PURGE`: Table renamed to e.g `_vt_PURGE_6ace8bcef73211ea87e9f875a4d24e90_20200916080539`. Vitess purges (or will purge, based on workload and prior engagements) rows from `PURGE` tables. Data is lost and the user may not resurrect the table anymore.
72+
- `HOLD`: Table is renamed to something like `_vt_hld_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_`. Vitess will not make changes to the table, will not drop data. The table is put away for safe keeping for X hours/days. If it turns out the app still needs the table, the user can `RENAME` is back to its original name, taking it out of this game.
73+
- `PURGE`: Table renamed to e.g `_vt_prg_6ace8bcef73211ea87e9f875a4d24e90_20200916080539_`. Vitess purges (or will purge, based on workload and prior engagements) rows from `PURGE` tables. Data is lost and the user may not resurrect the table anymore.
7474
Most likely we will settle for a `SQL_LOG_BIN=0`, ie purging will not go through replication. The replicas are not so badly affected by `DROP` statements as a `primary`.
75-
- `EVAC`: Table renamed to e.g. `_vt_EVAC_6ace8bcef73211ea87e9f875a4d24e90_20200918192031`. The table sits still for Y houtrs/days. I'm thinking this period will be pre-defined by vitess. The purpose of this state is to wait a _reasonable_ amount of time so that tabe's pages are evacuated from the innodb buffer pool by the natural succession of production IO/memory activity.
76-
- `DROP`: Table renamed to e.g. `_vt_DROP_6ace8bcef73211ea87e9f875a4d24e90_20200921193202`. Vitess will `DROP TABLE` this table _imminently_.
75+
- `EVAC`: Table renamed to e.g. `_vt_evc_6ace8bcef73211ea87e9f875a4d24e90_20200918192031_`. The table sits still for Y houtrs/days. I'm thinking this period will be pre-defined by vitess. The purpose of this state is to wait a _reasonable_ amount of time so that tabe's pages are evacuated from the innodb buffer pool by the natural succession of production IO/memory activity.
76+
- `DROP`: Table renamed to e.g. `_vt_drp_6ace8bcef73211ea87e9f875a4d24e90_20200921193202_`. Vitess will `DROP TABLE` this table _imminently_.
7777
- gone: end of lifecycle
7878

7979
## Transitioning and skipping of states

go/test/endtoend/tabletmanager/tablegc/tablegc_test.go

Lines changed: 86 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -320,137 +320,118 @@ func TestPopulateTable(t *testing.T) {
320320
validateTableDoesNotExist(t, "no_such_table")
321321
}
322322

323-
func generateRenameStatement(newFormat bool, fromTableName string, state schema.TableGCState, tm time.Time) (statement string, toTableName string, err error) {
324-
if newFormat {
325-
return schema.GenerateRenameStatement(fromTableName, state, tm)
326-
}
327-
return schema.GenerateRenameStatementOldFormat(fromTableName, state, tm)
323+
func generateRenameStatement(fromTableName string, state schema.TableGCState, tm time.Time) (statement string, toTableName string, err error) {
324+
return schema.GenerateRenameStatement(fromTableName, state, tm)
328325
}
329326

330327
func TestHold(t *testing.T) {
331-
for _, newNameFormat := range []bool{false, true} {
332-
t.Run(fmt.Sprintf("new format=%t", newNameFormat), func(t *testing.T) {
333-
populateTable(t)
334-
query, tableName, err := generateRenameStatement(newNameFormat, "t1", schema.HoldTableGCState, time.Now().UTC().Add(tableTransitionExpiration))
335-
assert.NoError(t, err)
328+
populateTable(t)
329+
query, tableName, err := generateRenameStatement("t1", schema.HoldTableGCState, time.Now().UTC().Add(tableTransitionExpiration))
330+
assert.NoError(t, err)
336331

337-
_, err = primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true)
338-
assert.NoError(t, err)
332+
_, err = primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true)
333+
assert.NoError(t, err)
339334

340-
validateTableDoesNotExist(t, "t1")
341-
validateTableExists(t, tableName)
335+
validateTableDoesNotExist(t, "t1")
336+
validateTableExists(t, tableName)
342337

343-
time.Sleep(tableTransitionExpiration / 2)
344-
{
345-
// Table was created with +10s timestamp, so it should still exist
346-
validateTableExists(t, tableName)
338+
time.Sleep(tableTransitionExpiration / 2)
339+
{
340+
// Table was created with +10s timestamp, so it should still exist
341+
validateTableExists(t, tableName)
347342

348-
checkTableRows(t, tableName, 1024)
349-
}
343+
checkTableRows(t, tableName, 1024)
344+
}
350345

351-
time.Sleep(tableTransitionExpiration)
352-
// We're now both beyond table's timestamp as well as a tableGC interval
353-
validateTableDoesNotExist(t, tableName)
354-
if fastDropTable {
355-
validateAnyState(t, -1, schema.DropTableGCState, schema.TableDroppedGCState)
356-
} else {
357-
validateAnyState(t, -1, schema.PurgeTableGCState, schema.EvacTableGCState, schema.DropTableGCState, schema.TableDroppedGCState)
358-
}
359-
})
346+
time.Sleep(tableTransitionExpiration)
347+
// We're now both beyond table's timestamp as well as a tableGC interval
348+
validateTableDoesNotExist(t, tableName)
349+
if fastDropTable {
350+
validateAnyState(t, -1, schema.DropTableGCState, schema.TableDroppedGCState)
351+
} else {
352+
validateAnyState(t, -1, schema.PurgeTableGCState, schema.EvacTableGCState, schema.DropTableGCState, schema.TableDroppedGCState)
360353
}
361354
}
362355

363356
func TestEvac(t *testing.T) {
364-
for _, newNameFormat := range []bool{false, true} {
365-
t.Run(fmt.Sprintf("new format=%t", newNameFormat), func(t *testing.T) {
366-
var tableName string
367-
t.Run("setting up EVAC table", func(t *testing.T) {
368-
populateTable(t)
369-
var query string
370-
var err error
371-
query, tableName, err = generateRenameStatement(newNameFormat, "t1", schema.EvacTableGCState, time.Now().UTC().Add(tableTransitionExpiration))
372-
assert.NoError(t, err)
373-
374-
_, err = primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true)
375-
assert.NoError(t, err)
376-
377-
validateTableDoesNotExist(t, "t1")
378-
})
379-
380-
t.Run("validating before expiration", func(t *testing.T) {
381-
time.Sleep(tableTransitionExpiration / 2)
382-
// Table was created with +10s timestamp, so it should still exist
383-
if fastDropTable {
384-
// EVAC state is skipped in mysql 8.0.23 and beyond
385-
validateTableDoesNotExist(t, tableName)
386-
} else {
387-
validateTableExists(t, tableName)
388-
checkTableRows(t, tableName, 1024)
389-
}
390-
})
391-
392-
t.Run("validating rows evacuated", func(t *testing.T) {
393-
// We're now both beyond table's timestamp as well as a tableGC interval
394-
validateTableDoesNotExist(t, tableName)
395-
// Table should be renamed as _vt_DROP_... and then dropped!
396-
validateAnyState(t, 0, schema.DropTableGCState, schema.TableDroppedGCState)
397-
})
398-
})
399-
}
357+
var tableName string
358+
t.Run("setting up EVAC table", func(t *testing.T) {
359+
populateTable(t)
360+
var query string
361+
var err error
362+
query, tableName, err = generateRenameStatement("t1", schema.EvacTableGCState, time.Now().UTC().Add(tableTransitionExpiration))
363+
assert.NoError(t, err)
364+
365+
_, err = primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true)
366+
assert.NoError(t, err)
367+
368+
validateTableDoesNotExist(t, "t1")
369+
})
370+
371+
t.Run("validating before expiration", func(t *testing.T) {
372+
time.Sleep(tableTransitionExpiration / 2)
373+
// Table was created with +10s timestamp, so it should still exist
374+
if fastDropTable {
375+
// EVAC state is skipped in mysql 8.0.23 and beyond
376+
validateTableDoesNotExist(t, tableName)
377+
} else {
378+
validateTableExists(t, tableName)
379+
checkTableRows(t, tableName, 1024)
380+
}
381+
})
382+
383+
t.Run("validating rows evacuated", func(t *testing.T) {
384+
// We're now both beyond table's timestamp as well as a tableGC interval
385+
validateTableDoesNotExist(t, tableName)
386+
// Table should be renamed as _vt_drp_... and then dropped!
387+
validateAnyState(t, 0, schema.DropTableGCState, schema.TableDroppedGCState)
388+
})
400389
}
401390

402391
func TestDrop(t *testing.T) {
403-
for _, newNameFormat := range []bool{false, true} {
404-
t.Run(fmt.Sprintf("new format=%t", newNameFormat), func(t *testing.T) {
405-
populateTable(t)
406-
query, tableName, err := generateRenameStatement(newNameFormat, "t1", schema.DropTableGCState, time.Now().UTC().Add(tableTransitionExpiration))
407-
assert.NoError(t, err)
392+
populateTable(t)
393+
query, tableName, err := generateRenameStatement("t1", schema.DropTableGCState, time.Now().UTC().Add(tableTransitionExpiration))
394+
assert.NoError(t, err)
408395

409-
_, err = primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true)
410-
assert.NoError(t, err)
396+
_, err = primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true)
397+
assert.NoError(t, err)
411398

412-
validateTableDoesNotExist(t, "t1")
399+
validateTableDoesNotExist(t, "t1")
413400

414-
time.Sleep(tableTransitionExpiration)
415-
time.Sleep(2 * gcCheckInterval)
416-
// We're now both beyond table's timestamp as well as a tableGC interval
417-
validateTableDoesNotExist(t, tableName)
418-
})
419-
}
401+
time.Sleep(tableTransitionExpiration)
402+
time.Sleep(2 * gcCheckInterval)
403+
// We're now both beyond table's timestamp as well as a tableGC interval
404+
validateTableDoesNotExist(t, tableName)
420405
}
421406

422407
func TestPurge(t *testing.T) {
423-
for _, newNameFormat := range []bool{false, true} {
424-
t.Run(fmt.Sprintf("new format=%t", newNameFormat), func(t *testing.T) {
425-
populateTable(t)
426-
query, tableName, err := generateRenameStatement(newNameFormat, "t1", schema.PurgeTableGCState, time.Now().UTC().Add(tableTransitionExpiration))
427-
require.NoError(t, err)
428-
429-
_, err = primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true)
430-
require.NoError(t, err)
431-
432-
validateTableDoesNotExist(t, "t1")
433-
if !fastDropTable {
434-
validateTableExists(t, tableName)
435-
checkTableRows(t, tableName, 1024)
436-
}
437-
if !fastDropTable {
438-
time.Sleep(5 * gcPurgeCheckInterval) // wait for table to be purged
439-
}
440-
validateTableDoesNotExist(t, tableName) // whether purged or not, table should at some point transition to next state
441-
if fastDropTable {
442-
// if MySQL supports fast DROP TABLE, TableGC completely skips the PURGE state. Rows are not purged.
443-
validateAnyState(t, 1024, schema.DropTableGCState, schema.TableDroppedGCState)
444-
} else {
445-
validateAnyState(t, 0, schema.EvacTableGCState, schema.DropTableGCState, schema.TableDroppedGCState)
446-
}
447-
})
408+
populateTable(t)
409+
query, tableName, err := generateRenameStatement("t1", schema.PurgeTableGCState, time.Now().UTC().Add(tableTransitionExpiration))
410+
require.NoError(t, err)
411+
412+
_, err = primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true)
413+
require.NoError(t, err)
414+
415+
validateTableDoesNotExist(t, "t1")
416+
if !fastDropTable {
417+
validateTableExists(t, tableName)
418+
checkTableRows(t, tableName, 1024)
419+
}
420+
if !fastDropTable {
421+
time.Sleep(5 * gcPurgeCheckInterval) // wait for table to be purged
422+
}
423+
validateTableDoesNotExist(t, tableName) // whether purged or not, table should at some point transition to next state
424+
if fastDropTable {
425+
// if MySQL supports fast DROP TABLE, TableGC completely skips the PURGE state. Rows are not purged.
426+
validateAnyState(t, 1024, schema.DropTableGCState, schema.TableDroppedGCState)
427+
} else {
428+
validateAnyState(t, 0, schema.EvacTableGCState, schema.DropTableGCState, schema.TableDroppedGCState)
448429
}
449430
}
450431

451432
func TestPurgeView(t *testing.T) {
452433
populateTable(t)
453-
query, tableName, err := generateRenameStatement(true, "v1", schema.PurgeTableGCState, time.Now().UTC().Add(tableTransitionExpiration))
434+
query, tableName, err := generateRenameStatement("v1", schema.PurgeTableGCState, time.Now().UTC().Add(tableTransitionExpiration))
454435
require.NoError(t, err)
455436

456437
_, err = primaryTablet.VttabletProcess.QueryTablet(query, keyspaceName, true)

go/test/endtoend/vreplication/config_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import (
3636
// 2. Column and table names with special characters in them, namely a dash
3737
// 3. Identifiers using reserved words, as lead is a reserved word in MySQL 8.0+ (https://dev.mysql.com/doc/refman/8.0/en/keywords.html)
3838
//
39-
// The internal table _vt_PURGE_4f9194b43b2011eb8a0104ed332e05c2_20221210194431 should be ignored by vreplication
39+
// The internal table _vt_prg_4f9194b43b2011eb8a0104ed332e05c2_20221210194431_ should be ignored by vreplication
4040
// The db_order_test table is used to ensure vreplication and vdiff work well with complex non-integer PKs, even across DB versions.
4141
// The db_order_test table needs to use a collation that exists in all versions for cross version tests as we use the collation for the PK
4242
// based merge sort in VDiff. The table is using a non-default collation for any version with utf8mb4 as 5.7 does NOT show the default
@@ -66,7 +66,7 @@ create table customer2(cid int, name varchar(128), typ enum('individual','soho',
6666
create table customer_seq2(id int, next_id bigint, cache bigint, primary key(id)) comment 'vitess_sequence';
6767
create table `+"`Lead`(`Lead-id`"+` binary(16), name varbinary(16), date1 datetime not null default '0000-00-00 00:00:00', date2 datetime not null default '2021-00-01 00:00:00', primary key (`+"`Lead-id`"+`), key (date1));
6868
create table `+"`Lead-1`(`Lead`"+` binary(16), name varbinary(16), date1 datetime not null default '0000-00-00 00:00:00', date2 datetime not null default '2021-00-01 00:00:00', primary key (`+"`Lead`"+`), key (date2));
69-
create table _vt_PURGE_4f9194b43b2011eb8a0104ed332e05c2_20221210194431(id int, val varbinary(128), primary key(id), key(val));
69+
create table _vt_prg_4f9194b43b2011eb8a0104ed332e05c2_20221210194431_(id int, val varbinary(128), primary key(id), key(val));
7070
create table db_order_test (c_uuid varchar(64) not null default '', created_at datetime not null, dstuff varchar(128), dtstuff text, dbstuff blob, cstuff char(32), primary key (c_uuid,created_at), key (dstuff)) CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
7171
create table vdiff_order (order_id varchar(50) collate utf8mb4_unicode_ci not null, primary key (order_id), key (order_id)) charset=utf8mb4 COLLATE=utf8mb4_unicode_ci;
7272
create table datze (id int, dt1 datetime not null default current_timestamp, dt2 datetime not null, ts1 timestamp default current_timestamp, primary key (id), key (dt1));
@@ -83,10 +83,10 @@ create table ukTable (id1 int not null, id2 int not null, name varchar(20), uniq
8383
internalSchema = `
8484
create table _1e275eef_3b20_11eb_a38f_04ed332e05c2_20201210204529_gho(id int, val varbinary(128), primary key(id));
8585
create table _0e8a27c8_1d73_11ec_a579_0aa0c75a6a1d_20210924200735_vrepl(id int, val varbinary(128), primary key(id));
86-
create table _vt_PURGE_1f9194b43b2011eb8a0104ed332e05c2_20201210194431(id int, val varbinary(128), primary key(id));
87-
create table _vt_EVAC_6ace8bcef73211ea87e9f875a4d24e90_29990915120410(id int, val varbinary(128), primary key(id));
88-
create table _vt_DROP_2bce8bcef73211ea87e9f875a4d24e90_20200915120410(id int, val varbinary(128), primary key(id));
89-
create table _vt_HOLD_4abe6bcef73211ea87e9f875a4d24e90_20220115120410(id int, val varbinary(128), primary key(id));
86+
create table _vt_prg_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_(id int, val varbinary(128), primary key(id));
87+
create table _vt_evc_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_(id int, val varbinary(128), primary key(id));
88+
create table _vt_drp_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_(id int, val varbinary(128), primary key(id));
89+
create table _vt_hld_6ace8bcef73211ea87e9f875a4d24e90_20200915120410_(id int, val varbinary(128), primary key(id));
9090
`
9191

9292
initialProductVSchema = `

go/test/endtoend/vreplication/resharding_workflows_v2_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,7 @@ func testMoveTablesV2Workflow(t *testing.T) {
596596

597597
// The purge table should get skipped/ignored
598598
// If it's not then we'll get an error as the table doesn't exist in the vschema
599-
createMoveTablesWorkflow(t, "customer,loadtest,vdiff_order,reftable,_vt_PURGE_4f9194b43b2011eb8a0104ed332e05c2_20221210194431")
599+
createMoveTablesWorkflow(t, "customer,loadtest,vdiff_order,reftable,_vt_prg_4f9194b43b2011eb8a0104ed332e05c2_20221210194431_")
600600
waitForWorkflowState(t, vc, defaultKsWorkflow, binlogdatapb.VReplicationWorkflowState_Running.String())
601601
validateReadsRouteToSource(t, "replica,rdonly")
602602
validateWritesRouteToSource(t)

0 commit comments

Comments
 (0)