Skip to content

Commit 9bcc3fd

Browse files
craig[bot]msbutler
andcommitted
Merge #156397
156397: backup: capture function descriptor changes in revision history backups r=jeffswenson a=msbutler Previously, backup would omit grabbing a function descriptor in revision history backups if the function did not exist at the end time or only within the interval. This patch fixes this bug. Fixes #156206 Release note: none Co-authored-by: Michael Butler <butler@cockroachlabs.com>
2 parents a6fc263 + 2b51945 commit 9bcc3fd

File tree

3 files changed

+127
-3
lines changed

3 files changed

+127
-3
lines changed

pkg/backup/backup_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11079,3 +11079,119 @@ func TestBackupIndexCreatedAfterBackup(t *testing.T) {
1107911079
require.NoError(t, err)
1108011080
require.Len(t, files, 5)
1108111081
}
11082+
11083+
// TestBackupRestoreFunctionDependenciesRevisionHistory tests that revision
11084+
// history backups and restores correctly handle function dependencies.
11085+
func TestBackupRestoreFunctionDependenciesRevisionHistory(t *testing.T) {
11086+
defer leaktest.AfterTest(t)()
11087+
defer log.Scope(t).Close(t)
11088+
11089+
const numAccounts = 0
11090+
_, sqlDB, _, cleanupFn := backupRestoreTestSetup(t, singleNode, numAccounts, InitManualReplication)
11091+
defer cleanupFn()
11092+
11093+
// Helper function to check which functions exist in a database.
11094+
checkFunctions := func(dbName string, expectedFuncs ...string) {
11095+
var expected [][]string
11096+
for _, fn := range expectedFuncs {
11097+
expected = append(expected, []string{fn})
11098+
}
11099+
if len(expected) > 0 {
11100+
sqlDB.CheckQueryResults(t,
11101+
fmt.Sprintf(`SELECT function_name FROM crdb_internal.create_function_statements WHERE database_name = '%s' ORDER BY function_name`, dbName),
11102+
expected)
11103+
} else {
11104+
sqlDB.CheckQueryResults(t,
11105+
fmt.Sprintf(`SELECT count(*) FROM crdb_internal.create_function_statements WHERE database_name = '%s'`, dbName),
11106+
[][]string{{"0"}})
11107+
}
11108+
11109+
}
11110+
11111+
// t0: Create database with parent and child functions where child depends on parent.
11112+
sqlDB.Exec(t, `CREATE DATABASE test_db`)
11113+
sqlDB.Exec(t, `USE test_db`)
11114+
sqlDB.Exec(t, `CREATE FUNCTION test_db.parent_func() RETURNS INT LANGUAGE SQL AS $$ SELECT 42 $$`)
11115+
sqlDB.Exec(t, `CREATE FUNCTION test_db.child_func() RETURNS INT LANGUAGE SQL AS $$ SELECT parent_func() + 1 $$`)
11116+
11117+
// Verify both functions exist.
11118+
checkFunctions("test_db", "child_func", "parent_func")
11119+
11120+
// T1: Full backup with revision history.
11121+
backupPath := "nodelocal://1/function_deps_backup"
11122+
sqlDB.Exec(t, `BACKUP DATABASE test_db INTO $1 WITH revision_history`, backupPath)
11123+
11124+
// T2: Capture timestamp after backup (when parent and child exist).
11125+
var t2 string
11126+
sqlDB.QueryRow(t, `SELECT cluster_logical_timestamp()`).Scan(&t2)
11127+
11128+
sqlDB.Exec(t, `DROP FUNCTION child_func`)
11129+
sqlDB.Exec(t, `CREATE FUNCTION test_db.child_func_2() RETURNS INT LANGUAGE SQL AS $$ SELECT parent_func() + 2 $$`)
11130+
11131+
checkFunctions("test_db", "child_func_2", "parent_func")
11132+
11133+
// T3: Capture timestamp after dropping child and creating child 2.
11134+
var t3 string
11135+
sqlDB.QueryRow(t, `SELECT cluster_logical_timestamp()`).Scan(&t3)
11136+
11137+
// Drop child 2 so it does not appear at time 4.
11138+
sqlDB.Exec(t, `DROP FUNCTION child_func_2`)
11139+
11140+
// T4: Incremental backup with revision history.
11141+
sqlDB.Exec(t, `BACKUP DATABASE test_db INTO LATEST IN $1 WITH revision_history`, backupPath)
11142+
11143+
// T5: Capture timestamp after incremental backup (parent and child 2).
11144+
var t5 string
11145+
sqlDB.QueryRow(t, `SELECT cluster_logical_timestamp()`).Scan(&t5)
11146+
11147+
sqlDB.Exec(t, `DROP FUNCTION parent_func`)
11148+
11149+
// Verify no functions exist.
11150+
checkFunctions("test_db")
11151+
11152+
// T6: Capture timestamp after dropping parent.
11153+
var t6 string
11154+
sqlDB.QueryRow(t, `SELECT cluster_logical_timestamp()`).Scan(&t6)
11155+
11156+
// T6.2: Take another incremental backup to capture parent function drop.
11157+
sqlDB.Exec(t, `BACKUP DATABASE test_db INTO LATEST IN $1 WITH revision_history`, backupPath)
11158+
11159+
// T7: Restore AOST t2 -> expect both parent and child functions.
11160+
restoreQuery := fmt.Sprintf(
11161+
"RESTORE DATABASE test_db FROM LATEST IN $1 AS OF SYSTEM TIME %s with new_db_name = test2", t2)
11162+
sqlDB.Exec(t, restoreQuery, backupPath)
11163+
sqlDB.Exec(t, `USE test2`)
11164+
11165+
checkFunctions("test2", "child_func", "parent_func")
11166+
11167+
sqlDB.CheckQueryResults(t, `SELECT child_func()`, [][]string{{"43"}})
11168+
11169+
// T8: Restore AOST t3 -> expect parent and child 2.
11170+
restoreQuery = fmt.Sprintf(
11171+
"RESTORE DATABASE test_db FROM LATEST IN $1 AS OF SYSTEM TIME %s with new_db_name = test3", t3)
11172+
sqlDB.Exec(t, restoreQuery, backupPath)
11173+
sqlDB.Exec(t, `USE test3`)
11174+
11175+
checkFunctions("test3", "child_func_2", "parent_func")
11176+
11177+
sqlDB.CheckQueryResults(t, `SELECT parent_func()`, [][]string{{"42"}})
11178+
sqlDB.CheckQueryResults(t, `SELECT child_func_2()`, [][]string{{"44"}})
11179+
11180+
// T9: Restore AOST t5 -> expect parent.
11181+
restoreQuery = fmt.Sprintf(
11182+
"RESTORE DATABASE test_db FROM LATEST IN $1 AS OF SYSTEM TIME %s with new_db_name = test5", t5)
11183+
sqlDB.Exec(t, restoreQuery, backupPath)
11184+
sqlDB.Exec(t, `USE test5`)
11185+
11186+
checkFunctions("test5", "parent_func")
11187+
11188+
sqlDB.CheckQueryResults(t, `SELECT parent_func()`, [][]string{{"42"}})
11189+
11190+
// T10: Restore AOST t6 -> expect no functions.
11191+
restoreQuery = fmt.Sprintf(
11192+
"RESTORE DATABASE test_db FROM LATEST IN $1 AS OF SYSTEM TIME %s with new_db_name = test6", t6)
11193+
sqlDB.Exec(t, restoreQuery, backupPath)
11194+
sqlDB.Exec(t, `USE test6`)
11195+
11196+
checkFunctions("test6")
11197+
}

pkg/backup/backupinfo/manifest_handling.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1158,11 +1158,15 @@ func LoadSQLDescsFromBackupsAtTime(
11581158
asOf = lastBackupManifest.EndTime
11591159
}
11601160

1161-
for _, b := range backupManifests {
1161+
// TODO(msbutler): this logic can be removed because we already crop backup
1162+
// manifests that are too new. Keeping this around in case there is a bug
1163+
// upstream.
1164+
for i, b := range backupManifests {
11621165
if asOf.Less(b.StartTime) {
11631166
break
11641167
}
11651168
lastBackupManifest = b
1169+
lastIterFactory = layerToBackupManifestFileIterFactory[i]
11661170
}
11671171

11681172
// From this point on we try to load descriptors based on descriptor

pkg/backup/targets.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,12 @@ func getRelevantDescChanges(
126126
}
127127
for _, i := range starting {
128128
switch desc := i.(type) {
129-
case catalog.TableDescriptor, catalog.TypeDescriptor, catalog.SchemaDescriptor:
129+
case catalog.TableDescriptor, catalog.TypeDescriptor, catalog.SchemaDescriptor, catalog.FunctionDescriptor:
130130
// We need to add to interestingIDs so that if we later see a delete for
131131
// this ID we still know it is interesting to us, even though we will not
132132
// have a parentID at that point (since the delete is a nil desc).
133+
//
134+
// In other words, descriptor exists at start time, but not at end time.
133135
if _, ok := interestingParents[desc.GetParentID()]; ok {
134136
interestingIDs[desc.GetID()] = struct{}{}
135137
}
@@ -159,7 +161,9 @@ func getRelevantDescChanges(
159161
} else if change.Desc != nil {
160162
desc := backupinfo.NewDescriptorForManifest(change.Desc)
161163
switch desc := desc.(type) {
162-
case catalog.TableDescriptor, catalog.TypeDescriptor, catalog.SchemaDescriptor:
164+
case catalog.TableDescriptor, catalog.TypeDescriptor, catalog.SchemaDescriptor, catalog.FunctionDescriptor:
165+
// In other words, does not exist at start or end time, but within the
166+
// interval.
163167
if _, ok := interestingParents[desc.GetParentID()]; ok {
164168
interestingIDs[desc.GetID()] = struct{}{}
165169
interestingChanges = append(interestingChanges, change)

0 commit comments

Comments
 (0)