// GetTableDescriptor retrieves a table descriptor directly from the KV layer. func GetTableDescriptor(kvDB *client.DB, database string, table string) *TableDescriptor { dbNameKey := MakeNameMetadataKey(keys.RootNamespaceID, database) gr, err := kvDB.Get(dbNameKey) if err != nil { panic(err) } if !gr.Exists() { panic("database missing") } dbDescID := ID(gr.ValueInt()) tableNameKey := MakeNameMetadataKey(dbDescID, table) gr, err = kvDB.Get(tableNameKey) if err != nil { panic(err) } if !gr.Exists() { panic("table missing") } descKey := MakeDescMetadataKey(ID(gr.ValueInt())) desc := &Descriptor{} if err := kvDB.GetProto(descKey, desc); err != nil { panic("proto missing") } return desc.GetTable() }
// concurrentIncrements starts two Goroutines in parallel, both of which // read the integers stored at the other's key and add it onto their own. // It is checked that the outcome is serializable, i.e. exactly one of the // two Goroutines (the later write) sees the previous write by the other. func concurrentIncrements(db *client.DB, t *testing.T) { // wgStart waits for all transactions to line up, wgEnd has the main // function wait for them to finish. var wgStart, wgEnd sync.WaitGroup wgStart.Add(2 + 1) wgEnd.Add(2) for i := 0; i < 2; i++ { go func(i int) { // Read the other key, write key i. readKey := []byte(fmt.Sprintf(testUser+"/value-%d", (i+1)%2)) writeKey := []byte(fmt.Sprintf(testUser+"/value-%d", i)) defer wgEnd.Done() wgStart.Done() // Wait until the other goroutines are running. wgStart.Wait() if pErr := db.Txn(func(txn *client.Txn) *roachpb.Error { txn.SetDebugName(fmt.Sprintf("test-%d", i), 0) // Retrieve the other key. gr, pErr := txn.Get(readKey) if pErr != nil { return pErr } otherValue := int64(0) if gr.Value != nil { otherValue = gr.ValueInt() } _, pErr = txn.Inc(writeKey, 1+otherValue) return pErr }); pErr != nil { t.Error(pErr) } }(i) } // Kick the goroutines loose. wgStart.Done() // Wait for the goroutines to finish. wgEnd.Wait() // Verify that both keys contain something and, more importantly, that // one key actually contains the value of the first writer and not only // its own. total := int64(0) results := []int64(nil) for i := 0; i < 2; i++ { readKey := []byte(fmt.Sprintf(testUser+"/value-%d", i)) gr, pErr := db.Get(readKey) if pErr != nil { t.Fatal(pErr) } if gr.Value == nil { t.Fatalf("unexpected empty key: %s=%v", readKey, gr.Value) } total += gr.ValueInt() results = append(results, gr.ValueInt()) } // First writer should have 1, second one 2 if total != 3 { t.Fatalf("got unserializable values %v", results) } }
// concurrentIncrements starts two Goroutines in parallel, both of which // read the integers stored at the other's key and add it onto their own. // It is checked that the outcome is serializable, i.e. exactly one of the // two Goroutines (the later write) sees the previous write by the other. // The isMultiphase option runs the transaction in multiple phases recreating // the transaction from the transaction protobuf returned from the server. func concurrentIncrements(db *client.DB, t *testing.T, isMultiphase bool) { // wgStart waits for all transactions to line up, wgEnd has the main // function wait for them to finish. var wgStart, wgEnd sync.WaitGroup wgStart.Add(2 + 1) wgEnd.Add(2) for i := 0; i < 2; i++ { go func(i int) { // Read the other key, write key i. readKey := []byte(fmt.Sprintf(testUser+"/value-%d", (i+1)%2)) writeKey := []byte(fmt.Sprintf(testUser+"/value-%d", i)) defer wgEnd.Done() wgStart.Done() // Wait until the other goroutines are running. wgStart.Wait() if isMultiphase { applyInc := func(txn *client.Txn) (error, proto.Transaction) { txn.SetDebugName(fmt.Sprintf("test-%d", i)) b := client.Batch{} // Retrieve the other key. b.Get(readKey) if err := txn.Run(&b); err != nil { return err, txn.GetState() } otherValue := int64(0) gr := b.Results[0].Rows[0] if gr.Value != nil { otherValue = gr.ValueInt() } // New txn. txn = db.ReconstructTxn(txn.GetState()) // Write our key. b = client.Batch{} b.Inc(writeKey, 1+otherValue) if err := txn.Run(&b); err != nil { return err, txn.GetState() } // New txn. txn = db.ReconstructTxn(txn.GetState()) err := txn.Commit(&client.Batch{}) return err, txn.GetState() } for r := retry.Start(client.DefaultTxnRetryOptions); r.Next(); { txn := db.ReconstructTxn(proto.Transaction{}) if err, txnProto := applyInc(txn); err != nil { // New txn. txn = db.ReconstructTxn(txnProto) if err := txn.Rollback(); err != nil { t.Error(err) } else { // retry continue } } // exit retry break } } else { if err := db.Txn(func(txn *client.Txn) error { txn.SetDebugName(fmt.Sprintf("test-%d", i)) // Retrieve the other key. gr, err := txn.Get(readKey) if err != nil { return err } otherValue := int64(0) if gr.Value != nil { otherValue = gr.ValueInt() } _, err = txn.Inc(writeKey, 1+otherValue) return err }); err != nil { t.Error(err) } } }(i) } // Kick the goroutines loose. wgStart.Done() // Wait for the goroutines to finish. wgEnd.Wait() // Verify that both keys contain something and, more importantly, that // one key actually contains the value of the first writer and not only // its own. total := int64(0) results := []int64(nil) for i := 0; i < 2; i++ { readKey := []byte(fmt.Sprintf(testUser+"/value-%d", i)) gr, err := db.Get(readKey) if err != nil { log.Fatal(err) } if gr.Value == nil { t.Fatalf("unexpected empty key: %s=%v", readKey, gr.Value) } total += gr.ValueInt() results = append(results, gr.ValueInt()) } // First writer should have 1, second one 2 if total != 3 { t.Fatalf("got unserializable values %v", results) } }