bigtable: Add ability to create table with initial splits

Note that creating initial splits is not normally necessary.
If, however, a large amount of data must be imported into the
table right after creation then creating ~100's of splits
across the expected key space can be beneficial for performance
until Bigtable figures out how to split the load naturally.

Change-Id: I531f194dab87056b77e2b950e5bd0a174264fbb5
Reviewed-on: https://code-review.googlesource.com/11290
Reviewed-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/bigtable/admin.go b/bigtable/admin.go
index f136e67..f65054e 100644
--- a/bigtable/admin.go
+++ b/bigtable/admin.go
@@ -104,6 +104,27 @@
 	return err
 }
 
+// CreatePresplitTable creates a new table in the instance.
+// The list of row keys will be used to initially split the table into multiple tablets.
+// Given two split keys, "s1" and "s2", three tablets will be created,
+// spanning the key ranges: [, s1), [s1, s2), [s2, ).
+// This method may return before the table's creation is complete.
+func (ac *AdminClient) CreatePresplitTable(ctx context.Context, table string, split_keys []string) error {
+	var req_splits []*btapb.CreateTableRequest_Split
+	for _, split := range split_keys {
+		req_splits = append(req_splits, &btapb.CreateTableRequest_Split{[]byte(split)})
+	}
+	ctx = mergeMetadata(ctx, ac.md)
+	prefix := ac.instancePrefix()
+	req := &btapb.CreateTableRequest{
+		Parent:        prefix,
+		TableId:       table,
+		InitialSplits: req_splits,
+	}
+	_, err := ac.tClient.CreateTable(ctx, req)
+	return err
+}
+
 // CreateColumnFamily creates a new column family in a table.
 func (ac *AdminClient) CreateColumnFamily(ctx context.Context, table, family string) error {
 	// TODO(dsymonds): Permit specifying gcexpr and any other family settings.
diff --git a/bigtable/cmd/cbt/cbt.go b/bigtable/cmd/cbt/cbt.go
index 85e3db4..4afd1e8 100644
--- a/bigtable/cmd/cbt/cbt.go
+++ b/bigtable/cmd/cbt/cbt.go
@@ -205,7 +205,9 @@
 		Name:     "createtable",
 		Desc:     "Create a table",
 		do:       doCreateTable,
-		Usage:    "cbt createtable <table>",
+		Usage:    "cbt createtable <table> [initial_splits...]\n" +
+			"  initial_splits=row		A row key to be used to initially split the table " +
+			"into multiple tablets. Can be repeated to create multiple splits.",
 		Required: cbtconfig.ProjectAndInstanceRequired,
 	},
 	{
@@ -335,10 +337,16 @@
 }
 
 func doCreateTable(ctx context.Context, args ...string) {
-	if len(args) != 1 {
-		log.Fatal("usage: cbt createtable <table>")
+	if len(args) < 1 {
+		log.Fatal("usage: cbt createtable <table> [initial_splits...]")
 	}
-	err := getAdminClient().CreateTable(ctx, args[0])
+	var err error
+	if len(args) > 1 {
+		splits := args[1:]
+		err = getAdminClient().CreatePresplitTable(ctx, args[0], splits)
+	} else {
+		err = getAdminClient().CreateTable(ctx, args[0])
+	}
 	if err != nil {
 		log.Fatalf("Creating table: %v", err)
 	}