From 74ec55732666ac67d406775cb4864615e4443c0e Mon Sep 17 00:00:00 2001 From: Claire Iacono Date: Fri, 8 Nov 2024 16:37:08 -0800 Subject: [PATCH] AdminClient: change snapshot table and apply snapshot to new table Add integration test to demonstrate cloning a snapshot to a new, not currently existing table, instead of restoring the snapshot to the table it was created from. --- admin_client.go | 2 +- hrpc/query_test.go | 2 +- hrpc/snapshot.go | 2 +- integration_test.go | 123 +++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 124 insertions(+), 5 deletions(-) diff --git a/admin_client.go b/admin_client.go index 3ff93e02..bc515bfa 100644 --- a/admin_client.go +++ b/admin_client.go @@ -24,7 +24,7 @@ const ( snaphotValidateInterval time.Duration = time.Second / 2 ) -// AdminClient to perform admistrative operations with HMaster +// AdminClient to perform administrative operations with HMaster type AdminClient interface { CreateTable(t *hrpc.CreateTable) error DeleteTable(t *hrpc.DeleteTable) error diff --git a/hrpc/query_test.go b/hrpc/query_test.go index 24602f19..46ff3227 100644 --- a/hrpc/query_test.go +++ b/hrpc/query_test.go @@ -17,7 +17,7 @@ import ( "github.com/tsuna/gohbase/test" ) -func TestFamilesOption(t *testing.T) { +func TestFamiliesOption(t *testing.T) { f := map[string][]string{"yolo": []string{"swag", "meow"}} g, err := NewGet(context.Background(), nil, nil, Families(f)) diff --git a/hrpc/snapshot.go b/hrpc/snapshot.go index 0f40305a..bae557b6 100644 --- a/hrpc/snapshot.go +++ b/hrpc/snapshot.go @@ -219,7 +219,7 @@ type RestoreSnapshot struct { *Snapshot } -// NewRestoreSnapshot creates a new RestoreSnapshot request that will delete +// NewRestoreSnapshot creates a new RestoreSnapshot request that will restore // the given snapshot. func NewRestoreSnapshot(t *Snapshot) *RestoreSnapshot { return &RestoreSnapshot{t} diff --git a/integration_test.go b/integration_test.go index 9d48a9d3..d41ff453 100644 --- a/integration_test.go +++ b/integration_test.go @@ -2279,9 +2279,9 @@ func TestSnapshot(t *testing.T) { } } -// Test snapshot restoration +// TestRestoreSnapshot tests using a snapshot to restore a table. func TestRestoreSnapshot(t *testing.T) { - // Prochedure for this test is roughly: + // Procedure for this test is roughly: // - Create some data in a table. // - Create a snapshot. // - Remove all data. @@ -2515,3 +2515,122 @@ func TestCacheRegions(t *testing.T) { } } + +// TestNewTableFromSnapshot tests the ability to create a snapshot from a table, +// and then use this snapshot to create a new, different table from the table the +// snapshot was created from. This is different from restoring the snapshot to the +// table it was created from. +func TestNewTableFromSnapshot(t *testing.T) { + var ( + key = t.Name() + "_Get" + snapshotName = "snapshot-" + table + ) + + c := gohbase.NewClient(*host, gohbase.RpcQueueSize(1)) + defer c.Close() + // Insert some data into the main test table. + if err := insertKeyValue(c, key, "cf", []byte{1}); err != nil { + t.Fatal(err) + } + + ac := gohbase.NewAdminClient(*host) + // Create snapshot from the main test table. + sn, err := hrpc.NewSnapshot(context.Background(), snapshotName, table) + if err != nil { + t.Fatal(err) + } + if err = ac.CreateSnapshot(sn); err != nil { + t.Fatal(err) + } + + defer func() { + err = ac.DeleteSnapshot(sn) + if err != nil { + t.Error(err) + } + }() + + // Delete the data from main test table after taking snapshot. + if err = deleteKeyValue(c, key, "cf", []byte{1}); err != nil { + t.Fatal(err) + } + // Confirm data has been deleted. + gMain, err := hrpc.NewGetStr(context.Background(), table, key) + if err != nil { + t.Fatal(err) + } + + r, err := c.Get(gMain) + if err != nil { + t.Fatal(err) + } + + if len(r.Cells) != 0 { + t.Fatalf("expected no cells in table %s key %s", table, key) + } + + // Restore the snapshot of the same name to a new table. + // The new table doesn't exist yet, HBase will create it when trying to restore a snapshot to a + // table that does not already exist. If the snapshot table doesn't exist, as in this case, + // HBase will clone the snapshot to a new table. + tableNew := fmt.Sprintf("gohbase_test_%d_%s", time.Now().UnixNano(), t.Name()) + sn, err = hrpc.NewSnapshot(context.Background(), snapshotName, tableNew) + if err != nil { + t.Fatal(err) + } + if err = ac.RestoreSnapshot(sn); err != nil { + t.Fatal(err) + } + + // It may take some time for the new table with the restored data to be created, + // wait some time for this to complete. + var tn *hrpc.ListTableNames + ctx, _ := context.WithTimeout(context.Background(), 30*time.Second) + tn, err = hrpc.NewListTableNames(ctx, hrpc.ListRegex(tableNew)) + for { + var names []*pb.TableName + names, err = ac.ListTableNames(tn) + if err != nil { + t.Fatal(err) + } + if len(names) != 0 { + break + } + time.Sleep(1 * time.Second) + } + + // Now that this table has been created, clean up after test. + defer func() { + err = DeleteTable(ac, tableNew) + if err != nil { + t.Fatal(err) + } + }() + + // Check that the snapshot data has been cloned to the new table. + gNew, err := hrpc.NewGetStr(context.Background(), tableNew, key) + if err != nil { + t.Fatal(err) + } + r, err = c.Get(gNew) + if err != nil { + t.Fatal(err) + } + if len(r.Cells) == 0 { + t.Fatal("Expected non-empty result") + } + expV := []byte{1} + if !bytes.Equal(r.Cells[0].Value, expV) { + t.Fatalf("expected %v, got %v:", expV, r.Cells[0].Value) + } + + // Checking that the data did not get restored to the main test table: + r, err = c.Get(gMain) + if err != nil { + t.Fatal(err) + } + + if len(r.Cells) != 0 { + t.Fatalf("expected no cells after RestoreSnapshot in table %s key %s", table, key) + } +}