// StoreData writes the supplied time series data to the cockroach server. // Stored data will be sampled at the supplied resolution. func (db *DB) StoreData(ctx context.Context, r Resolution, data []tspb.TimeSeriesData) error { var kvs []roachpb.KeyValue // Process data collection: data is converted to internal format, and a key // is generated for each internal message. for _, d := range data { idatas, err := d.ToInternal(r.SlabDuration(), r.SampleDuration()) if err != nil { return err } for _, idata := range idatas { var value roachpb.Value if err := value.SetProto(&idata); err != nil { return err } kvs = append(kvs, roachpb.KeyValue{ Key: MakeDataKey(d.Name, d.Source, r, idata.StartTimestampNanos), Value: value, }) } } // Send the individual internal merge requests. b := &client.Batch{} for _, kv := range kvs { b.AddRawRequest(&roachpb.MergeRequest{ Span: roachpb.Span{ Key: kv.Key, }, Value: kv.Value, }) } return db.db.Run(ctx, b) }
// Create the key/value pairs for the default zone config entry. func createDefaultZoneConfig() []roachpb.KeyValue { var ret []roachpb.KeyValue value := roachpb.Value{} desc := config.DefaultZoneConfig() if err := value.SetProto(&desc); err != nil { log.Fatalf(context.TODO(), "could not marshal %v", desc) } ret = append(ret, roachpb.KeyValue{ Key: MakeZoneKey(keys.RootNamespaceID), Value: value, }) return ret }
// BenchmarkMVCCMergeTimeSeries computes performance of merging time series data. // Uses an in-memory engine. func BenchmarkMVCCMergeTimeSeries_RocksDB(b *testing.B) { ts := &roachpb.InternalTimeSeriesData{ StartTimestampNanos: 0, SampleDurationNanos: 1000, Samples: []roachpb.InternalTimeSeriesSample{ {Offset: 0, Count: 1, Sum: 5.0}, }, } var value roachpb.Value if err := value.SetProto(ts); err != nil { b.Fatal(err) } runMVCCMerge(setupMVCCInMemRocksDB, &value, 1024, b) }
// GetInitialValues returns the set of initial K/V values which should be added to // a bootstrapping CockroachDB cluster in order to create the tables contained // in the schema. func (ms MetadataSchema) GetInitialValues() []roachpb.KeyValue { var ret []roachpb.KeyValue // Save the ID generator value, which will generate descriptor IDs for user // objects. value := roachpb.Value{} value.SetInt(int64(keys.MaxReservedDescID + 1)) ret = append(ret, roachpb.KeyValue{ Key: keys.DescIDGenerator, Value: value, }) // addDescriptor generates the needed KeyValue objects to install a // descriptor on a new cluster. addDescriptor := func(parentID ID, desc DescriptorProto) { // Create name metadata key. value := roachpb.Value{} value.SetInt(int64(desc.GetID())) ret = append(ret, roachpb.KeyValue{ Key: MakeNameMetadataKey(parentID, desc.GetName()), Value: value, }) // Create descriptor metadata key. value = roachpb.Value{} wrappedDesc := WrapDescriptor(desc) if err := value.SetProto(wrappedDesc); err != nil { log.Fatalf(context.TODO(), "could not marshal %v", desc) } ret = append(ret, roachpb.KeyValue{ Key: MakeDescMetadataKey(desc.GetID()), Value: value, }) } // Generate initial values for system databases and tables, which have // static descriptors that were generated elsewhere. for _, sysObj := range ms.descs { addDescriptor(sysObj.parentID, sysObj.desc) } // Other key/value generation that doesn't fit into databases and // tables. This can be used to add initial entries to a table. ret = append(ret, ms.otherKV...) // Sort returned key values; this is valuable because it matches the way the // objects would be sorted if read from the engine. sort.Sort(roachpb.KeyValueByKey(ret)) return ret }
// append the given entries to the raft log. Takes the previous values of // r.mu.lastIndex and r.mu.raftLogSize, and returns new values. We do this // rather than modifying them directly because these modifications need to be // atomic with the commit of the batch. func (r *Replica) append( ctx context.Context, batch engine.ReadWriter, prevLastIndex uint64, prevRaftLogSize int64, entries []raftpb.Entry, ) (uint64, int64, error) { if len(entries) == 0 { return prevLastIndex, prevRaftLogSize, nil } var diff enginepb.MVCCStats var value roachpb.Value for i := range entries { ent := &entries[i] key := keys.RaftLogKey(r.RangeID, ent.Index) if err := value.SetProto(ent); err != nil { return 0, 0, err } value.InitChecksum(key) var err error if ent.Index > prevLastIndex { err = engine.MVCCBlindPut(ctx, batch, &diff, key, hlc.ZeroTimestamp, value, nil /* txn */) } else { err = engine.MVCCPut(ctx, batch, &diff, key, hlc.ZeroTimestamp, value, nil /* txn */) } if err != nil { return 0, 0, err } } // Delete any previously appended log entries which never committed. lastIndex := entries[len(entries)-1].Index for i := lastIndex + 1; i <= prevLastIndex; i++ { err := engine.MVCCDelete(ctx, batch, &diff, keys.RaftLogKey(r.RangeID, i), hlc.ZeroTimestamp, nil /* txn */) if err != nil { return 0, 0, err } } if err := setLastIndex(ctx, batch, r.RangeID, lastIndex); err != nil { return 0, 0, err } raftLogSize := prevRaftLogSize + diff.SysBytes return lastIndex, raftLogSize, nil }
// Benchmark batch time series merge operations. This benchmark does not // perform any reads and is only used to measure the cost of the periodic time // series updates. func runMVCCBatchTimeSeries(emk engineMaker, batchSize int, b *testing.B) { // Precompute keys so we don't waste time formatting them at each iteration. numKeys := batchSize keys := make([]roachpb.Key, numKeys) for i := 0; i < numKeys; i++ { keys[i] = roachpb.Key(fmt.Sprintf("key-%d", i)) } // We always write the same time series data (containing a single unchanging // sample). This isn't realistic but is fine because we're never reading the // data. var value roachpb.Value if err := value.SetProto(&roachpb.InternalTimeSeriesData{ StartTimestampNanos: 0, SampleDurationNanos: 1000, Samples: []roachpb.InternalTimeSeriesSample{ {Offset: 0, Count: 1, Sum: 5.0}, }, }); err != nil { b.Fatal(err) } eng := emk(b, fmt.Sprintf("batch_merge_%d", batchSize)) defer eng.Close() b.ResetTimer() ts := hlc.Timestamp{} for i := 0; i < b.N; i++ { batch := eng.NewBatch() for j := 0; j < batchSize; j++ { ts.Logical++ if err := MVCCMerge(context.Background(), batch, nil, keys[j], ts, value); err != nil { b.Fatalf("failed put: %s", err) } } if err := batch.Commit(); err != nil { b.Fatal(err) } batch.Close() } b.StopTimer() }
// MergeInternalTimeSeriesData exports the engine's C++ merge logic for // InternalTimeSeriesData to higher level packages. This is intended primarily // for consumption by high level testing of time series functionality. func MergeInternalTimeSeriesData( sources ...roachpb.InternalTimeSeriesData, ) (roachpb.InternalTimeSeriesData, error) { // Wrap each proto in an inlined MVCC value, and marshal each wrapped value // to bytes. This is the format required by the engine. srcBytes := make([][]byte, 0, len(sources)) for _, src := range sources { var val roachpb.Value if err := val.SetProto(&src); err != nil { return roachpb.InternalTimeSeriesData{}, err } bytes, err := protoutil.Marshal(&enginepb.MVCCMetadata{ RawBytes: val.RawBytes, }) if err != nil { return roachpb.InternalTimeSeriesData{}, err } srcBytes = append(srcBytes, bytes) } // Merge every element into a nil byte slice, one at a time. var ( mergedBytes []byte err error ) for _, bytes := range srcBytes { mergedBytes, err = goMerge(mergedBytes, bytes) if err != nil { return roachpb.InternalTimeSeriesData{}, err } } // Unmarshal merged bytes and extract the time series value within. var meta enginepb.MVCCMetadata if err := proto.Unmarshal(mergedBytes, &meta); err != nil { return roachpb.InternalTimeSeriesData{}, err } mergedTS, err := MakeValue(meta).GetTimeseries() if err != nil { return roachpb.InternalTimeSeriesData{}, err } return mergedTS, nil }
func (tm *testModel) storeInModel(r Resolution, data tspb.TimeSeriesData) { // Note the source, used to construct keys for model queries. tm.seenSources[data.Source] = struct{}{} // Process and store data in the model. internalData, err := data.ToInternal(r.SlabDuration(), r.SampleDuration()) if err != nil { tm.t.Fatalf("test could not convert time series to internal format: %s", err.Error()) } for _, idata := range internalData { key := MakeDataKey(data.Name, data.Source, r, idata.StartTimestampNanos) keyStr := string(key) existing, ok := tm.modelData[keyStr] var newTs roachpb.InternalTimeSeriesData if ok { existingTs, err := existing.GetTimeseries() if err != nil { tm.t.Fatalf("test could not extract time series from existing model value: %s", err.Error()) } newTs, err = engine.MergeInternalTimeSeriesData(existingTs, idata) if err != nil { tm.t.Fatalf("test could not merge time series into model value: %s", err.Error()) } } else { newTs, err = engine.MergeInternalTimeSeriesData(idata) if err != nil { tm.t.Fatalf("test could not merge time series into model value: %s", err.Error()) } } var val roachpb.Value if err := val.SetProto(&newTs); err != nil { tm.t.Fatal(err) } tm.modelData[keyStr] = val } }
func timeSeriesAsValue(start int64, duration int64, samples ...tsSample) roachpb.Value { ts := &roachpb.InternalTimeSeriesData{ StartTimestampNanos: start, SampleDurationNanos: duration, } for _, sample := range samples { newSample := roachpb.InternalTimeSeriesSample{ Offset: sample.offset, Count: sample.count, Sum: sample.sum, } if sample.count > 1 { newSample.Max = proto.Float64(sample.max) newSample.Min = proto.Float64(sample.min) } ts.Samples = append(ts.Samples, newSample) } var v roachpb.Value if err := v.SetProto(ts); err != nil { panic(err) } return v }
func TestMigrateZoneConfig(t *testing.T) { defer leaktest.AfterTest(t)() testCases := []struct { input, want proto.Message }{ { &ZoneConfig{ ReplicaAttrs: []roachpb.Attributes{ {Attrs: []string{"foo"}}, {}, {}, }, }, &ZoneConfig{ NumReplicas: 3, Constraints: Constraints{ Constraints: []Constraint{ { Type: Constraint_POSITIVE, Value: "foo", }, }, }, }, }, { &ZoneConfig{ NumReplicas: 3, Constraints: Constraints{ Constraints: []Constraint{ { Type: Constraint_POSITIVE, Value: "foo", }, }, }, }, &ZoneConfig{ NumReplicas: 3, Constraints: Constraints{ Constraints: []Constraint{ { Type: Constraint_POSITIVE, Value: "foo", }, }, }, }, }, } for i, tc := range testCases { var val roachpb.Value if err := val.SetProto(tc.input); err != nil { t.Fatal(err) } out, err := MigrateZoneConfig(&val) if err != nil { t.Fatal(err) } if !proto.Equal(tc.want, &out) { t.Errorf("%d: MigrateZoneConfig(%+v) = %+v; not %+v", i, tc.input, out, tc.want) } } }
// marshalValue returns a roachpb.Value initialized from the source // interface{}, returning an error if the types are not compatible. func marshalValue(v interface{}) (roachpb.Value, error) { var r roachpb.Value // Handle a few common types via a type switch. switch t := v.(type) { case *roachpb.Value: return *t, nil case nil: return r, nil case bool: r.SetBool(t) return r, nil case string: r.SetBytes([]byte(t)) return r, nil case []byte: r.SetBytes(t) return r, nil case inf.Dec: err := r.SetDecimal(&t) return r, err case roachpb.Key: r.SetBytes([]byte(t)) return r, nil case time.Time: r.SetTime(t) return r, nil case duration.Duration: err := r.SetDuration(t) return r, err case proto.Message: err := r.SetProto(t) return r, err case roachpb.Value: panic("unexpected type roachpb.Value (use *roachpb.Value)") } // Handle all of the Go primitive types besides struct and pointers. This // switch also handles types based on a primitive type (e.g. "type MyInt // int"). switch v := reflect.ValueOf(v); v.Kind() { case reflect.Bool: r.SetBool(v.Bool()) return r, nil case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: r.SetInt(v.Int()) return r, nil case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: r.SetInt(int64(v.Uint())) return r, nil case reflect.Float32, reflect.Float64: r.SetFloat(v.Float()) return r, nil case reflect.String: r.SetBytes([]byte(v.String())) return r, nil } return r, fmt.Errorf("unable to marshal %T: %v", v, v) }
func TestValidateCrossTableReferences(t *testing.T) { defer leaktest.AfterTest(t)() s, _, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) defer s.Stopper().Stop() tests := []struct { err string desc TableDescriptor referenced []TableDescriptor }{ // Foreign keys { err: `invalid foreign key: missing table=52 index=2: descriptor not found`, desc: TableDescriptor{ ID: 51, PrimaryIndex: IndexDescriptor{ ID: 1, ForeignKey: ForeignKeyReference{Table: 52, Index: 2}, }, }, referenced: nil, }, { err: `invalid foreign key: missing table=baz index=2: index-id "2" does not exist`, desc: TableDescriptor{ ID: 51, PrimaryIndex: IndexDescriptor{ ID: 1, ForeignKey: ForeignKeyReference{Table: 52, Index: 2}, }, }, referenced: []TableDescriptor{{ ID: 52, Name: "baz", }}, }, { err: `missing fk back reference to foo.bar from baz.qux`, desc: TableDescriptor{ ID: 51, Name: "foo", PrimaryIndex: IndexDescriptor{ ID: 1, Name: "bar", ForeignKey: ForeignKeyReference{Table: 52, Index: 2}, }, }, referenced: []TableDescriptor{{ ID: 52, Name: "baz", PrimaryIndex: IndexDescriptor{ ID: 2, Name: "qux", }, }}, }, { err: `invalid fk backreference table=52 index=2: descriptor not found`, desc: TableDescriptor{ ID: 51, PrimaryIndex: IndexDescriptor{ ID: 1, ReferencedBy: []ForeignKeyReference{{Table: 52, Index: 2}}, }, }, }, { err: `invalid fk backreference table=baz index=2: index-id "2" does not exist`, desc: TableDescriptor{ ID: 51, PrimaryIndex: IndexDescriptor{ ID: 1, ReferencedBy: []ForeignKeyReference{{Table: 52, Index: 2}}, }, }, referenced: []TableDescriptor{{ ID: 52, Name: "baz", }}, }, { err: `broken fk backward reference from foo.bar to baz.qux`, desc: TableDescriptor{ ID: 51, Name: "foo", PrimaryIndex: IndexDescriptor{ ID: 1, Name: "bar", ReferencedBy: []ForeignKeyReference{{Table: 52, Index: 2}}, }, }, referenced: []TableDescriptor{{ ID: 52, Name: "baz", PrimaryIndex: IndexDescriptor{ ID: 2, Name: "qux", }, }}, }, // Interleaves { err: `invalid interleave: missing table=52 index=2: descriptor not found`, desc: TableDescriptor{ ID: 51, PrimaryIndex: IndexDescriptor{ ID: 1, Interleave: InterleaveDescriptor{Ancestors: []InterleaveDescriptor_Ancestor{ {TableID: 52, IndexID: 2}, }}, }, }, referenced: nil, }, { err: `invalid interleave: missing table=baz index=2: index-id "2" does not exist`, desc: TableDescriptor{ ID: 51, PrimaryIndex: IndexDescriptor{ ID: 1, Interleave: InterleaveDescriptor{Ancestors: []InterleaveDescriptor_Ancestor{ {TableID: 52, IndexID: 2}, }}, }, }, referenced: []TableDescriptor{{ ID: 52, Name: "baz", }}, }, { err: `missing interleave back reference to foo.bar from baz.qux`, desc: TableDescriptor{ ID: 51, Name: "foo", PrimaryIndex: IndexDescriptor{ ID: 1, Name: "bar", Interleave: InterleaveDescriptor{Ancestors: []InterleaveDescriptor_Ancestor{ {TableID: 52, IndexID: 2}, }}, }, }, referenced: []TableDescriptor{{ ID: 52, Name: "baz", PrimaryIndex: IndexDescriptor{ ID: 2, Name: "qux", }, }}, }, { err: `invalid interleave backreference table=52 index=2: descriptor not found`, desc: TableDescriptor{ ID: 51, PrimaryIndex: IndexDescriptor{ ID: 1, InterleavedBy: []ForeignKeyReference{{Table: 52, Index: 2}}, }, }, }, { err: `invalid interleave backreference table=baz index=2: index-id "2" does not exist`, desc: TableDescriptor{ ID: 51, PrimaryIndex: IndexDescriptor{ ID: 1, InterleavedBy: []ForeignKeyReference{{Table: 52, Index: 2}}, }, }, referenced: []TableDescriptor{{ ID: 52, Name: "baz", }}, }, { err: `broken interleave backward reference from foo.bar to baz.qux`, desc: TableDescriptor{ ID: 51, Name: "foo", PrimaryIndex: IndexDescriptor{ ID: 1, Name: "bar", InterleavedBy: []ForeignKeyReference{{Table: 52, Index: 2}}, }, }, referenced: []TableDescriptor{{ ID: 52, Name: "baz", PrimaryIndex: IndexDescriptor{ ID: 2, Name: "qux", }, }}, }, } for i, test := range tests { for _, referencedDesc := range test.referenced { var v roachpb.Value desc := &Descriptor{Union: &Descriptor_Table{Table: &referencedDesc}} if err := v.SetProto(desc); err != nil { t.Fatal(err) } if err := kvDB.Put(context.TODO(), MakeDescMetadataKey(referencedDesc.ID), &v); err != nil { t.Fatal(err) } } txn := client.NewTxn(context.Background(), *kvDB) if err := test.desc.validateCrossReferences(txn); err == nil { t.Errorf("%d: expected \"%s\", but found success: %+v", i, test.err, test.desc) } else if test.err != err.Error() { t.Errorf("%d: expected \"%s\", but found \"%s\"", i, test.err, err.Error()) } for _, referencedDesc := range test.referenced { if err := kvDB.Del(context.TODO(), MakeDescMetadataKey(referencedDesc.ID)); err != nil { t.Fatal(err) } } } }