diff --git a/src/cluster/kv/fake/store.go b/src/cluster/kv/fake/store.go new file mode 100644 index 0000000000..b0d6c34a3c --- /dev/null +++ b/src/cluster/kv/fake/store.go @@ -0,0 +1,156 @@ +// Copyright (c) 2020 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package fake + +import ( + "errors" + + "github.com/m3db/m3/src/cluster/kv" + + "github.com/golang/protobuf/proto" +) + +// NewStore returns a fakeStore adhering to the kv.Store interface. +// All methods except Watch are implemented. Implementation is not threadsafe +// and should only be used for tests. +func NewStore() kv.Store { + return &fakeStore{ + store: make(map[string][]kv.Value), + } +} + +type fakeStore struct { + store map[string][]kv.Value +} + +func (f *fakeStore) Get(key string) (kv.Value, error) { + value, ok := f.store[key] + if !ok { + return nil, kv.ErrNotFound + } + + return value[len(value)-1], nil +} + +func (f *fakeStore) Watch(_ string) (kv.ValueWatch, error) { + panic("implement me") +} + +func (f *fakeStore) Set(key string, v proto.Message) (int, error) { + oldVal, err := f.Get(key) + if err != nil && err != kv.ErrNotFound { + return 0, err + } + + data, err := proto.Marshal(v) + if err != nil { + return 0, err + } + + var newVer int + if oldVal == nil { + f.store[key] = []kv.Value{newValue(data, int64(newVer))} + } else { + newVer = oldVal.Version() + 1 + f.store[key] = append(f.store[key], newValue(data, int64(newVer))) + } + + return newVer, nil +} + +func (f *fakeStore) SetIfNotExists(key string, v proto.Message) (int, error) { + _, err := f.Get(key) + if err == kv.ErrNotFound { + return f.Set(key, v) + } else { + return 0, kv.ErrAlreadyExists + } +} + +func (f *fakeStore) CheckAndSet(key string, version int, v proto.Message) (int, error) { + val, err := f.Get(key) + if err != nil && err != kv.ErrNotFound { + return 0, err + } else if err != nil && err == kv.ErrNotFound && version == 0 { + return f.Set(key, v) + } else if val == nil { + return 0, kv.ErrVersionMismatch + } else if val.Version() == version { + return f.Set(key, v) + } else { + return 0, kv.ErrVersionMismatch + } +} + +func (f *fakeStore) Delete(key string) (kv.Value, error) { + val, err := f.Get(key) + if err != nil { + return nil, err + } + delete(f.store, key) + + return val, nil +} + +func (f *fakeStore) History(key string, from, to int) ([]kv.Value, error) { + if from > to || from < 0 || to < 0 { + return nil, errors.New("invalid history range") + } + + if from == to { + return nil, nil + } + + vals, ok := f.store[key] + if !ok { + return nil, kv.ErrNotFound + } + if to > len(vals) { + return nil, errors.New("invalid history range") + } + + return vals[from:to], nil +} + +type value struct { + Val []byte + Ver int64 +} + +func newValue(val []byte, ver int64) *value { + return &value{ + Val: val, + Ver: ver, + } +} + +func (c *value) IsNewer(other kv.Value) bool { + return c.Version() > other.Version() +} + +func (c *value) Unmarshal(v proto.Message) error { + err := proto.Unmarshal(c.Val, v) + return err +} + +func (c *value) Version() int { + return int(c.Ver) +} diff --git a/src/query/api/v1/handler/database/create.go b/src/query/api/v1/handler/database/create.go index ab672cb9ad..7ca6de5970 100644 --- a/src/query/api/v1/handler/database/create.go +++ b/src/query/api/v1/handler/database/create.go @@ -185,7 +185,7 @@ func (h *createHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - parsedReq, namespaceRequest, placementRequest, rErr := h.parseAndValidateRequest(r, currPlacement) + parsedReq, namespaceRequests, placementRequest, rErr := h.parseAndValidateRequest(r, currPlacement) if rErr != nil { logger.Error("unable to parse request", zap.Error(rErr)) xhttp.Error(w, rErr.Inner(), rErr.Code()) @@ -213,21 +213,24 @@ func (h *createHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // TODO(rartoul): Add test for NS exists. - _, nsExists := nsRegistry.Namespaces[namespaceRequest.Name] - if nsExists { - err := fmt.Errorf( - "unable to create namespace: %s because it already exists", - namespaceRequest.Name) - logger.Error("unable to create namespace", zap.Error(err)) - xhttp.Error(w, err, http.StatusBadRequest) - return + for _, namespaceRequest := range namespaceRequests { + if _, nsExists := nsRegistry.Namespaces[namespaceRequest.Name]; nsExists { + err := fmt.Errorf( + "unable to create namespace: %s because it already exists", + namespaceRequest.Name) + logger.Error("unable to create namespace", zap.Error(err)) + xhttp.Error(w, err, http.StatusBadRequest) + return + } } - nsRegistry, err = h.namespaceAddHandler.Add(namespaceRequest, opts) - if err != nil { - logger.Error("unable to add namespace", zap.Error(err)) - xhttp.Error(w, err, http.StatusInternalServerError) - return + for _, namespaceRequest := range namespaceRequests { + nsRegistry, err = h.namespaceAddHandler.Add(namespaceRequest, opts) + if err != nil { + logger.Error("unable to add namespace", zap.Error(err)) + xhttp.Error(w, err, http.StatusInternalServerError) + return + } } placementProto, err := currPlacement.Proto() @@ -313,7 +316,7 @@ func (h *createHandler) maybeInitPlacement( func (h *createHandler) parseAndValidateRequest( r *http.Request, existingPlacement clusterplacement.Placement, -) (*admin.DatabaseCreateRequest, *admin.NamespaceAddRequest, *admin.PlacementInitRequest, *xhttp.ParseError) { +) (*admin.DatabaseCreateRequest, []*admin.NamespaceAddRequest, *admin.PlacementInitRequest, *xhttp.ParseError) { requirePlacement := existingPlacement == nil defer r.Body.Close() @@ -340,7 +343,7 @@ func (h *createHandler) parseAndValidateRequest( return nil, nil, nil, xhttp.NewParseError(errMissingRequiredField, http.StatusBadRequest) } - namespaceAddRequest, err := defaultedNamespaceAddRequest(dbCreateReq, existingPlacement) + namespaceAddRequests, err := defaultedNamespaceAddRequests(dbCreateReq, existingPlacement) if err != nil { return nil, nil, nil, xhttp.NewParseError(err, http.StatusBadRequest) } @@ -354,15 +357,14 @@ func (h *createHandler) parseAndValidateRequest( } } - return dbCreateReq, namespaceAddRequest, placementInitRequest, nil + return dbCreateReq, namespaceAddRequests, placementInitRequest, nil } -func defaultedNamespaceAddRequest( +func defaultedNamespaceAddRequests( r *admin.DatabaseCreateRequest, existingPlacement clusterplacement.Placement, -) (*admin.NamespaceAddRequest, error) { +) ([]*admin.NamespaceAddRequest, error) { var ( - opts = dbnamespace.NewOptions() dbType = dbType(r.Type) ) if dbType == "" && existingPlacement != nil { @@ -375,86 +377,180 @@ func defaultedNamespaceAddRequest( } } + nsAddRequests := make([]*admin.NamespaceAddRequest, 0, 2) switch dbType { case dbTypeLocal, dbTypeCluster: - opts = opts.SetRepairEnabled(false) - retentionOpts := opts.RetentionOptions() - - if r.RetentionTime == "" { - retentionOpts = retentionOpts.SetRetentionPeriod(defaultLocalRetentionPeriod) - } else { - value, err := time.ParseDuration(r.RetentionTime) - if err != nil { - return nil, fmt.Errorf("invalid retention time: %v", err) - } - retentionOpts = retentionOpts.SetRetentionPeriod(value) + unaggregatedNs, err := defaultedUnaggregatedNamespaceAddRequest(r) + if err != nil { + return nil, err } + nsAddRequests = append(nsAddRequests, unaggregatedNs) - retentionPeriod := retentionOpts.RetentionPeriod() + aggregatedNs, err := defaultedAggregatedNamespaceAddRequest(r) + if err != nil { + return nil, err + } + if aggregatedNs != nil { + nsAddRequests = append(nsAddRequests, aggregatedNs) + } + default: + return nil, errInvalidDBType + } - var blockSize time.Duration - switch { - case r.BlockSize != nil && r.BlockSize.Time != "": - value, err := time.ParseDuration(r.BlockSize.Time) - if err != nil { - return nil, fmt.Errorf("invalid block size time: %v", err) - } - blockSize = value - - case r.BlockSize != nil && r.BlockSize.ExpectedSeriesDatapointsPerHour > 0: - value := r.BlockSize.ExpectedSeriesDatapointsPerHour - blockSize = time.Duration(blockSizeFromExpectedSeriesScalar / value) - // Snap to the nearest 5mins - blockSizeCeil := blockSize.Truncate(5*time.Minute) + 5*time.Minute - blockSizeFloor := blockSize.Truncate(5 * time.Minute) - if blockSizeFloor%time.Hour == 0 || - blockSizeFloor%30*time.Minute == 0 || - blockSizeFloor%15*time.Minute == 0 || - blockSizeFloor%10*time.Minute == 0 { - // Try snap to hour or 30min or 15min or 10min boundary if possible - blockSize = blockSizeFloor - } else { - blockSize = blockSizeCeil - } + return nsAddRequests, nil +} - if blockSize < minRecommendCalculateBlockSize { - blockSize = minRecommendCalculateBlockSize - } else if blockSize > maxRecommendCalculateBlockSize { - blockSize = maxRecommendCalculateBlockSize - } +func defaultedUnaggregatedNamespaceAddRequest( + r *admin.DatabaseCreateRequest, +) (*admin.NamespaceAddRequest, error) { + opts := dbnamespace.NewOptions(). + SetRepairEnabled(false) + retentionOpts := opts.RetentionOptions() + + if r.RetentionTime == "" { + retentionOpts = retentionOpts.SetRetentionPeriod(defaultLocalRetentionPeriod) + } else { + value, err := time.ParseDuration(r.RetentionTime) + if err != nil { + return nil, fmt.Errorf("invalid retention time: %v", err) + } + retentionOpts = retentionOpts.SetRetentionPeriod(value) + } - default: - // Use the maximum block size if we don't find a way to - // recommended one based on request parameters - max := recommendedBlockSizesByRetentionAsc[len(recommendedBlockSizesByRetentionAsc)-1] - blockSize = max.blockSize - for _, elem := range recommendedBlockSizesByRetentionAsc { - if retentionPeriod <= elem.forRetentionLessThanOrEqual { - blockSize = elem.blockSize - break - } - } + retentionPeriod := retentionOpts.RetentionPeriod() + var blockSize time.Duration + switch { + case r.BlockSize != nil && r.BlockSize.Time != "": + value, err := time.ParseDuration(r.BlockSize.Time) + if err != nil { + return nil, fmt.Errorf("invalid block size time: %v", err) + } + blockSize = value + + case r.BlockSize != nil && r.BlockSize.ExpectedSeriesDatapointsPerHour > 0: + value := r.BlockSize.ExpectedSeriesDatapointsPerHour + blockSize = time.Duration(blockSizeFromExpectedSeriesScalar / value) + // Snap to the nearest 5mins + blockSizeCeil := blockSize.Truncate(5*time.Minute) + 5*time.Minute + blockSizeFloor := blockSize.Truncate(5 * time.Minute) + if blockSizeFloor%time.Hour == 0 || + blockSizeFloor%30*time.Minute == 0 || + blockSizeFloor%15*time.Minute == 0 || + blockSizeFloor%10*time.Minute == 0 { + // Try snap to hour or 30min or 15min or 10min boundary if possible + blockSize = blockSizeFloor + } else { + blockSize = blockSizeCeil } - retentionOpts = retentionOpts.SetBlockSize(blockSize) - - indexOpts := opts.IndexOptions(). - SetEnabled(true). - SetBlockSize(blockSize) + if blockSize < minRecommendCalculateBlockSize { + blockSize = minRecommendCalculateBlockSize + } else if blockSize > maxRecommendCalculateBlockSize { + blockSize = maxRecommendCalculateBlockSize + } - opts = opts.SetRetentionOptions(retentionOpts). - SetIndexOptions(indexOpts) default: - return nil, errInvalidDBType + blockSize = getRecommendedBlockSize(retentionPeriod) } + retentionOpts = retentionOpts.SetBlockSize(blockSize) + + indexOpts := opts.IndexOptions(). + SetEnabled(true). + SetBlockSize(blockSize) + + opts = opts.SetRetentionOptions(retentionOpts). + SetIndexOptions(indexOpts) + + // Resolution does not apply to unaggregated namespaces so set to 0. + opts = opts.SetAggregationOptions(dbnamespace.NewAggregationOptions(). + SetAggregations([]dbnamespace.Aggregation{ + dbnamespace.NewUnaggregatedAggregation(), + })) + return &admin.NamespaceAddRequest{ Name: r.NamespaceName, Options: dbnamespace.OptionsToProto(opts), }, nil } +func defaultedAggregatedNamespaceAddRequest( + r *admin.DatabaseCreateRequest, +) (*admin.NamespaceAddRequest, error) { + agg := r.AggregatedNamespace + if agg == nil { + return nil, nil + } + + if agg.Name == "" { + return nil, errors.New("name required when aggregatedNamespace is set") + } + + if agg.Resolution == "" { + return nil, errors.New("resolution required when aggregatedNamespace is set") + } + + if agg.RetentionTime == "" { + return nil, errors.New("retention_time required when aggregatedNamespace is set") + } + + opts := dbnamespace.NewOptions(). + SetRepairEnabled(false) + + retentionOpts := opts.RetentionOptions() + retentionPeriod, err := time.ParseDuration(agg.RetentionTime) + if err != nil { + return nil, fmt.Errorf("invalid retention time: %v", err) + } + retentionOpts = retentionOpts.SetRetentionPeriod(retentionPeriod) + + blockSize := getRecommendedBlockSize(retentionPeriod) + + retentionOpts = retentionOpts.SetBlockSize(blockSize) + + indexOpts := opts.IndexOptions(). + SetEnabled(true). + SetBlockSize(blockSize) + + opts = opts.SetRetentionOptions(retentionOpts). + SetIndexOptions(indexOpts) + + resolution, err := time.ParseDuration(agg.Resolution) + if err != nil { + return nil, fmt.Errorf("invalid resolution: %v", err) + } + + attrs, err := dbnamespace.NewAggregatedAttributes(resolution, dbnamespace.NewDownsampleOptions(true)) + if err != nil { + return nil, err + } + + opts = opts.SetAggregationOptions(dbnamespace.NewAggregationOptions(). + SetAggregations([]dbnamespace.Aggregation{ + dbnamespace.NewAggregatedAggregation(attrs), + })) + + return &admin.NamespaceAddRequest{ + Name: agg.Name, + Options: dbnamespace.OptionsToProto(opts), + }, nil +} + +func getRecommendedBlockSize(retentionPeriod time.Duration) time.Duration { + // Use the maximum block size if we don't find a way to + // recommended one based on request parameters + max := recommendedBlockSizesByRetentionAsc[len(recommendedBlockSizesByRetentionAsc)-1] + blockSize := max.blockSize + for _, elem := range recommendedBlockSizesByRetentionAsc { + if retentionPeriod <= elem.forRetentionLessThanOrEqual { + blockSize = elem.blockSize + break + } + } + return blockSize +} + func defaultedPlacementInitRequest( r *admin.DatabaseCreateRequest, embeddedDbCfg *dbconfig.DBConfiguration, diff --git a/src/query/api/v1/handler/database/create_test.go b/src/query/api/v1/handler/database/create_test.go index 6d5f303854..7af4557985 100644 --- a/src/query/api/v1/handler/database/create_test.go +++ b/src/query/api/v1/handler/database/create_test.go @@ -30,6 +30,8 @@ import ( "testing" "time" + "github.com/m3db/m3/src/cluster/kv/fake" + "github.com/m3db/m3/src/cluster/client" "github.com/m3db/m3/src/cluster/generated/proto/placementpb" "github.com/m3db/m3/src/cluster/kv" @@ -151,7 +153,14 @@ func testLocalType(t *testing.T, providedType string, placementExists bool) { "registry": { "namespaces": { "testNamespace": { - "aggregationOptions": null, + "aggregationOptions": { + "aggregations": [ + { + "aggregated": false, + "attributes": null + } + ] + }, "bootstrapEnabled": true, "cacheBlocksOnRetrieve": true, "flushEnabled": true, @@ -316,7 +325,14 @@ func TestLocalTypeWithNumShards(t *testing.T) { "registry": { "namespaces": { "testNamespace": { - "aggregationOptions": null, + "aggregationOptions": { + "aggregations": [ + { + "aggregated": false, + "attributes": null + } + ] + }, "bootstrapEnabled": true, "cacheBlocksOnRetrieve": true, "flushEnabled": true, @@ -378,6 +394,7 @@ func TestLocalTypeWithNumShards(t *testing.T) { assert.Equal(t, expected, actual, xtest.Diff(expected, actual)) } + func TestLocalWithBlockSizeNanos(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -433,7 +450,14 @@ func TestLocalWithBlockSizeNanos(t *testing.T) { "registry": { "namespaces": { "testNamespace": { - "aggregationOptions": null, + "aggregationOptions": { + "aggregations": [ + { + "aggregated": false, + "attributes": null + } + ] + }, "bootstrapEnabled": true, "cacheBlocksOnRetrieve": true, "flushEnabled": true, @@ -556,7 +580,14 @@ func TestLocalWithBlockSizeExpectedSeriesDatapointsPerHour(t *testing.T) { "registry": { "namespaces": { "testNamespace": { - "aggregationOptions": null, + "aggregationOptions": { + "aggregations": [ + { + "aggregated": false, + "attributes": null + } + ] + }, "bootstrapEnabled": true, "cacheBlocksOnRetrieve": true, "flushEnabled": true, @@ -809,7 +840,14 @@ func testClusterTypeHosts(t *testing.T, placementExists bool) { "registry": { "namespaces": { "testNamespace": { - "aggregationOptions": null, + "aggregationOptions": { + "aggregations": [ + { + "aggregated": false, + "attributes": null + } + ] + }, "bootstrapEnabled": true, "cacheBlocksOnRetrieve": true, "flushEnabled": true, @@ -955,7 +993,14 @@ func TestClusterTypeHostsWithIsolationGroup(t *testing.T) { "registry": { "namespaces": { "testNamespace": { - "aggregationOptions": null, + "aggregationOptions": { + "aggregations": [ + { + "aggregated": false, + "attributes": null + } + ] + }, "bootstrapEnabled": true, "cacheBlocksOnRetrieve": true, "flushEnabled": true, @@ -1102,3 +1147,169 @@ func TestBadType(t *testing.T) { ), xtest.MustPrettyJSONString(t, string(body))) } + +func TestLocalTypeWithAggregatedNamespace(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockClient, _, mockPlacementService := SetupDatabaseTest(t, ctrl) + fakeKV := fake.NewStore() + mockClient.EXPECT().Store(gomock.Any()).Return(fakeKV, nil).AnyTimes() + createHandler, err := NewCreateHandler(mockClient, config.Configuration{}, + testDBCfg, svcDefaultOptions, instrument.NewOptions()) + require.NoError(t, err) + + w := httptest.NewRecorder() + + jsonInput := xjson.Map{ + "namespaceName": "testNamespace", + "type": "local", + "aggregatedNamespace": xjson.Map{ + "name": "testAggregatedNamespace", + "resolution": "5m", + "retentionTime": "2440h", + }, + } + + req := httptest.NewRequest("POST", "/database/create", + xjson.MustNewTestReader(t, jsonInput)) + require.NotNil(t, req) + + placementProto := &placementpb.Placement{ + Instances: map[string]*placementpb.Instance{ + "localhost": &placementpb.Instance{ + Id: "m3db_local", + IsolationGroup: "local", + Zone: "embedded", + Weight: 1, + Endpoint: "http://localhost:9000", + Hostname: "localhost", + Port: 9000, + }, + }, + } + newPlacement, err := placement.NewPlacementFromProto(placementProto) + require.NoError(t, err) + mockPlacementService.EXPECT().Placement().Return(nil, kv.ErrNotFound) + mockPlacementService.EXPECT().BuildInitialPlacement(gomock.Any(), 64, 1).Return(newPlacement, nil) + + createHandler.ServeHTTP(w, req) + + resp := w.Result() + body, err := ioutil.ReadAll(resp.Body) + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + expectedResponse := ` + { + "namespace": { + "registry": { + "namespaces": { + "testNamespace": { + "aggregationOptions": { + "aggregations": [ + { + "aggregated": false, + "attributes": null + } + ] + }, + "bootstrapEnabled": true, + "cacheBlocksOnRetrieve": true, + "flushEnabled": true, + "writesToCommitLog": true, + "cleanupEnabled": true, + "repairEnabled": false, + "retentionOptions": { + "retentionPeriodNanos": "86400000000000", + "blockSizeNanos": "3600000000000", + "bufferFutureNanos": "120000000000", + "bufferPastNanos": "600000000000", + "blockDataExpiry": true, + "blockDataExpiryAfterNotAccessPeriodNanos": "300000000000", + "futureRetentionPeriodNanos": "0" + }, + "snapshotEnabled": true, + "indexOptions": { + "enabled": true, + "blockSizeNanos": "3600000000000" + }, + "runtimeOptions": null, + "schemaOptions": null, + "coldWritesEnabled": false + }, + "testAggregatedNamespace": { + "aggregationOptions": { + "aggregations": [ + { + "aggregated": true, + "attributes": { + "resolutionNanos": "300000000000", + "downsampleOptions": { + "all": true + } + } + } + ] + }, + "bootstrapEnabled": true, + "cacheBlocksOnRetrieve": true, + "flushEnabled": true, + "writesToCommitLog": true, + "cleanupEnabled": true, + "repairEnabled": false, + "retentionOptions": { + "retentionPeriodNanos": "8784000000000000", + "blockSizeNanos": "86400000000000", + "bufferFutureNanos": "120000000000", + "bufferPastNanos": "600000000000", + "blockDataExpiry": true, + "blockDataExpiryAfterNotAccessPeriodNanos": "300000000000", + "futureRetentionPeriodNanos": "0" + }, + "snapshotEnabled": true, + "indexOptions": { + "enabled": true, + "blockSizeNanos": "86400000000000" + }, + "runtimeOptions": null, + "schemaOptions": null, + "coldWritesEnabled": false + } + } + } + }, + "placement": { + "placement": { + "instances": { + "m3db_local": { + "id": "m3db_local", + "isolationGroup": "local", + "zone": "embedded", + "weight": 1, + "endpoint": "http://localhost:9000", + "shards": [], + "shardSetId": 0, + "hostname": "localhost", + "port": 9000, + "metadata": { + "debugPort": 0 + } + } + }, + "replicaFactor": 0, + "numShards": 0, + "isSharded": false, + "cutoverTime": "0", + "isMirrored": false, + "maxShardSetId": 0 + }, + "version": 0 + } + } + ` + expected := xtest.MustPrettyJSONString(t, expectedResponse) + actual := xtest.MustPrettyJSONString(t, string(body)) + + assert.Equal(t, expected, actual, xtest.Diff(expected, actual)) +} diff --git a/src/query/generated/proto/admin/database.pb.go b/src/query/generated/proto/admin/database.pb.go index 1cae5048e9..ddc26b5e2c 100644 --- a/src/query/generated/proto/admin/database.pb.go +++ b/src/query/generated/proto/admin/database.pb.go @@ -32,6 +32,7 @@ It has these top-level messages: DatabaseCreateRequest + AggregatedNamespace BlockSize Host DatabaseCreateResponse @@ -86,6 +87,8 @@ type DatabaseCreateRequest struct { BlockSize *BlockSize `protobuf:"bytes,6,opt,name=block_size,json=blockSize" json:"block_size,omitempty"` // Required if not using local database type Hosts []*Host `protobuf:"bytes,7,rep,name=hosts" json:"hosts,omitempty"` + // Optional field to add an additional aggregated namespace. + AggregatedNamespace *AggregatedNamespace `protobuf:"bytes,8,opt,name=aggregated_namespace,json=aggregatedNamespace" json:"aggregated_namespace,omitempty"` } func (m *DatabaseCreateRequest) Reset() { *m = DatabaseCreateRequest{} } @@ -142,6 +145,50 @@ func (m *DatabaseCreateRequest) GetHosts() []*Host { return nil } +func (m *DatabaseCreateRequest) GetAggregatedNamespace() *AggregatedNamespace { + if m != nil { + return m.AggregatedNamespace + } + return nil +} + +type AggregatedNamespace struct { + // Required fields. + // Namespace name. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Time window to aggregate data points across. + // Uses same shorthand as retention_time. + Resolution string `protobuf:"bytes,2,opt,name=resolution,proto3" json:"resolution,omitempty"` + // Length of time to retain data. + RetentionTime string `protobuf:"bytes,3,opt,name=retention_time,json=retentionTime,proto3" json:"retention_time,omitempty"` +} + +func (m *AggregatedNamespace) Reset() { *m = AggregatedNamespace{} } +func (m *AggregatedNamespace) String() string { return proto.CompactTextString(m) } +func (*AggregatedNamespace) ProtoMessage() {} +func (*AggregatedNamespace) Descriptor() ([]byte, []int) { return fileDescriptorDatabase, []int{1} } + +func (m *AggregatedNamespace) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *AggregatedNamespace) GetResolution() string { + if m != nil { + return m.Resolution + } + return "" +} + +func (m *AggregatedNamespace) GetRetentionTime() string { + if m != nil { + return m.RetentionTime + } + return "" +} + type BlockSize struct { // Explicit block size using time shorthand, e.g. "2h" Time string `protobuf:"bytes,1,opt,name=time,proto3" json:"time,omitempty"` @@ -152,7 +199,7 @@ type BlockSize struct { func (m *BlockSize) Reset() { *m = BlockSize{} } func (m *BlockSize) String() string { return proto.CompactTextString(m) } func (*BlockSize) ProtoMessage() {} -func (*BlockSize) Descriptor() ([]byte, []int) { return fileDescriptorDatabase, []int{1} } +func (*BlockSize) Descriptor() ([]byte, []int) { return fileDescriptorDatabase, []int{2} } func (m *BlockSize) GetTime() string { if m != nil { @@ -189,7 +236,7 @@ type Host struct { func (m *Host) Reset() { *m = Host{} } func (m *Host) String() string { return proto.CompactTextString(m) } func (*Host) ProtoMessage() {} -func (*Host) Descriptor() ([]byte, []int) { return fileDescriptorDatabase, []int{2} } +func (*Host) Descriptor() ([]byte, []int) { return fileDescriptorDatabase, []int{3} } func (m *Host) GetId() string { if m != nil { @@ -241,7 +288,7 @@ type DatabaseCreateResponse struct { func (m *DatabaseCreateResponse) Reset() { *m = DatabaseCreateResponse{} } func (m *DatabaseCreateResponse) String() string { return proto.CompactTextString(m) } func (*DatabaseCreateResponse) ProtoMessage() {} -func (*DatabaseCreateResponse) Descriptor() ([]byte, []int) { return fileDescriptorDatabase, []int{3} } +func (*DatabaseCreateResponse) Descriptor() ([]byte, []int) { return fileDescriptorDatabase, []int{4} } func (m *DatabaseCreateResponse) GetNamespace() *NamespaceGetResponse { if m != nil { @@ -259,6 +306,7 @@ func (m *DatabaseCreateResponse) GetPlacement() *PlacementGetResponse { func init() { proto.RegisterType((*DatabaseCreateRequest)(nil), "admin.DatabaseCreateRequest") + proto.RegisterType((*AggregatedNamespace)(nil), "admin.AggregatedNamespace") proto.RegisterType((*BlockSize)(nil), "admin.BlockSize") proto.RegisterType((*Host)(nil), "admin.Host") proto.RegisterType((*DatabaseCreateResponse)(nil), "admin.DatabaseCreateResponse") @@ -328,6 +376,52 @@ func (m *DatabaseCreateRequest) MarshalTo(dAtA []byte) (int, error) { i += n } } + if m.AggregatedNamespace != nil { + dAtA[i] = 0x42 + i++ + i = encodeVarintDatabase(dAtA, i, uint64(m.AggregatedNamespace.Size())) + n2, err := m.AggregatedNamespace.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n2 + } + return i, nil +} + +func (m *AggregatedNamespace) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AggregatedNamespace) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Name) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintDatabase(dAtA, i, uint64(len(m.Name))) + i += copy(dAtA[i:], m.Name) + } + if len(m.Resolution) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintDatabase(dAtA, i, uint64(len(m.Resolution))) + i += copy(dAtA[i:], m.Resolution) + } + if len(m.RetentionTime) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintDatabase(dAtA, i, uint64(len(m.RetentionTime))) + i += copy(dAtA[i:], m.RetentionTime) + } return i, nil } @@ -431,21 +525,21 @@ func (m *DatabaseCreateResponse) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintDatabase(dAtA, i, uint64(m.Namespace.Size())) - n2, err := m.Namespace.MarshalTo(dAtA[i:]) + n3, err := m.Namespace.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n2 + i += n3 } if m.Placement != nil { dAtA[i] = 0x12 i++ i = encodeVarintDatabase(dAtA, i, uint64(m.Placement.Size())) - n3, err := m.Placement.MarshalTo(dAtA[i:]) + n4, err := m.Placement.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n3 + i += n4 } return i, nil } @@ -490,6 +584,28 @@ func (m *DatabaseCreateRequest) Size() (n int) { n += 1 + l + sovDatabase(uint64(l)) } } + if m.AggregatedNamespace != nil { + l = m.AggregatedNamespace.Size() + n += 1 + l + sovDatabase(uint64(l)) + } + return n +} + +func (m *AggregatedNamespace) Size() (n int) { + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovDatabase(uint64(l)) + } + l = len(m.Resolution) + if l > 0 { + n += 1 + l + sovDatabase(uint64(l)) + } + l = len(m.RetentionTime) + if l > 0 { + n += 1 + l + sovDatabase(uint64(l)) + } return n } @@ -779,6 +895,176 @@ func (m *DatabaseCreateRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AggregatedNamespace", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDatabase + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDatabase + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AggregatedNamespace == nil { + m.AggregatedNamespace = &AggregatedNamespace{} + } + if err := m.AggregatedNamespace.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDatabase(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDatabase + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AggregatedNamespace) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDatabase + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AggregatedNamespace: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AggregatedNamespace: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDatabase + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDatabase + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Resolution", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDatabase + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDatabase + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Resolution = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RetentionTime", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDatabase + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDatabase + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RetentionTime = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipDatabase(dAtA[iNdEx:]) @@ -1328,37 +1614,41 @@ func init() { } var fileDescriptorDatabase = []byte{ - // 510 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x52, 0xcb, 0x6e, 0xd3, 0x40, - 0x14, 0xc5, 0x79, 0x55, 0x9e, 0x28, 0xa1, 0x8c, 0x44, 0x65, 0x81, 0x08, 0x21, 0x08, 0x91, 0x0d, - 0xb1, 0x94, 0xac, 0x58, 0x12, 0x2a, 0xda, 0x05, 0xaa, 0x2a, 0x87, 0xbd, 0x35, 0xb6, 0x2f, 0xc9, - 0x88, 0xcc, 0xa3, 0x33, 0x63, 0x41, 0xf3, 0x11, 0x88, 0x2d, 0xe2, 0x87, 0x58, 0xf2, 0x09, 0x28, - 0xfc, 0x08, 0xf2, 0x8d, 0x3d, 0x85, 0x2e, 0xbb, 0x3b, 0x73, 0xee, 0xb9, 0xaf, 0x33, 0x97, 0xbc, - 0x59, 0x73, 0xb7, 0x29, 0xb3, 0x59, 0xae, 0x44, 0x2c, 0x16, 0x45, 0x16, 0x8b, 0x45, 0x6c, 0x4d, - 0x1e, 0x5f, 0x95, 0x60, 0xae, 0xe3, 0x35, 0x48, 0x30, 0xcc, 0x41, 0x11, 0x6b, 0xa3, 0x9c, 0x8a, - 0x59, 0x21, 0xb8, 0x8c, 0x0b, 0xe6, 0x58, 0xc6, 0x2c, 0xcc, 0x90, 0xa4, 0x5d, 0x64, 0x1f, 0x2d, - 0xef, 0x50, 0x49, 0x32, 0x01, 0x56, 0xb3, 0xbc, 0x2e, 0x75, 0xa7, 0x1a, 0x7a, 0xcb, 0x72, 0x10, - 0x20, 0xdd, 0xa1, 0xc6, 0xe4, 0x47, 0x8b, 0x3c, 0x3c, 0xad, 0x27, 0x7c, 0x6b, 0x80, 0x39, 0x48, - 0xe0, 0xaa, 0x04, 0xeb, 0xe8, 0x0b, 0x32, 0xf4, 0x0d, 0xd3, 0x0a, 0x45, 0xc1, 0x38, 0x98, 0x86, - 0xc9, 0xc0, 0xb3, 0x17, 0x4c, 0x00, 0xa5, 0xa4, 0xe3, 0xae, 0x35, 0x44, 0x2d, 0x0c, 0x22, 0xa6, - 0x4f, 0x08, 0x91, 0xa5, 0x48, 0xed, 0x86, 0x99, 0xc2, 0x46, 0xed, 0x71, 0x30, 0xed, 0x26, 0xa1, - 0x2c, 0xc5, 0x0a, 0x09, 0xfa, 0x8a, 0x50, 0x03, 0x7a, 0xcb, 0x73, 0xe6, 0xb8, 0x92, 0xe9, 0x47, - 0x96, 0x3b, 0x65, 0xa2, 0x0e, 0xca, 0x1e, 0xfc, 0x13, 0x79, 0x87, 0x81, 0x6a, 0x10, 0x03, 0x0e, - 0x24, 0x8a, 0x1d, 0x17, 0x10, 0x75, 0x0f, 0x83, 0x78, 0xf6, 0x03, 0x17, 0x40, 0x63, 0x42, 0xb2, - 0xad, 0xca, 0x3f, 0xa5, 0x96, 0xef, 0x20, 0xea, 0x8d, 0x83, 0x69, 0x7f, 0x7e, 0x3c, 0xc3, 0xad, - 0x67, 0xcb, 0x2a, 0xb0, 0xe2, 0x3b, 0x48, 0xc2, 0xac, 0x81, 0xf4, 0x19, 0xe9, 0x6e, 0x94, 0x75, - 0x36, 0x3a, 0x1a, 0xb7, 0xa7, 0xfd, 0x79, 0xbf, 0xd6, 0x9e, 0x2b, 0xeb, 0x92, 0x43, 0x64, 0x22, - 0x48, 0xe8, 0x53, 0x71, 0x53, 0xee, 0x6d, 0x40, 0x4c, 0xdf, 0x93, 0xe7, 0xf0, 0x45, 0x43, 0xee, - 0xa0, 0x48, 0x2d, 0x18, 0x0e, 0x36, 0xad, 0xfe, 0x5b, 0x2b, 0x2e, 0x9d, 0x4d, 0x35, 0x98, 0x74, - 0xa3, 0x4a, 0x83, 0xe6, 0xb4, 0x93, 0xa7, 0x8d, 0x74, 0x85, 0xca, 0x53, 0x2f, 0xbc, 0x04, 0x73, - 0xae, 0x4a, 0x33, 0xf9, 0x1e, 0x90, 0x4e, 0xd5, 0x9e, 0x0e, 0x49, 0x8b, 0x17, 0x75, 0xa3, 0x16, - 0x2f, 0x68, 0x44, 0x8e, 0x58, 0x51, 0x18, 0xb0, 0xb6, 0xf6, 0xb9, 0x79, 0x56, 0x43, 0x69, 0x65, - 0x1c, 0x9a, 0x3c, 0x48, 0x10, 0xd3, 0x97, 0xe4, 0x3e, 0xb7, 0x6a, 0x7b, 0x70, 0x77, 0x6d, 0x54, - 0xa9, 0xd1, 0xdc, 0x30, 0x19, 0x7a, 0xfa, 0xac, 0x62, 0xab, 0xe4, 0x9d, 0x92, 0x8d, 0x9f, 0x88, - 0xe9, 0x09, 0xe9, 0x7d, 0x06, 0xbe, 0xde, 0x38, 0xb4, 0x70, 0x90, 0xd4, 0xaf, 0xc9, 0xd7, 0x80, - 0x9c, 0xdc, 0x3e, 0x14, 0xab, 0x95, 0xb4, 0x40, 0x5f, 0x93, 0xd0, 0xdf, 0x04, 0x0e, 0xdd, 0x9f, - 0x3f, 0xae, 0xcd, 0xbc, 0x68, 0xf8, 0x33, 0x70, 0x8d, 0x3e, 0xb9, 0x51, 0x57, 0xa9, 0xfe, 0x22, - 0x71, 0xb5, 0x9b, 0xd4, 0xcb, 0x86, 0xff, 0x2f, 0xd5, 0xab, 0x97, 0xc7, 0x3f, 0xf7, 0xa3, 0xe0, - 0xd7, 0x7e, 0x14, 0xfc, 0xde, 0x8f, 0x82, 0x6f, 0x7f, 0x46, 0xf7, 0xb2, 0x1e, 0x9e, 0xf4, 0xe2, - 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc2, 0xec, 0x9c, 0x42, 0xa6, 0x03, 0x00, 0x00, + // 572 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x53, 0xc1, 0x6e, 0xd3, 0x40, + 0x10, 0xc5, 0x49, 0xd3, 0xe2, 0xa9, 0x5a, 0xca, 0x16, 0x2a, 0xab, 0x88, 0x10, 0x82, 0x10, 0xb9, + 0x10, 0x4b, 0xcd, 0x89, 0x63, 0x43, 0x45, 0x7b, 0x80, 0xaa, 0x72, 0xb8, 0x5b, 0x6b, 0x7b, 0x70, + 0x56, 0x64, 0xbd, 0xdb, 0xdd, 0xb5, 0xa0, 0xf9, 0x08, 0xc4, 0x95, 0x3f, 0xe2, 0xc8, 0x27, 0xa0, + 0x70, 0xe3, 0x2b, 0xd0, 0x6e, 0x6c, 0x27, 0x40, 0x4e, 0xbd, 0x8d, 0xdf, 0xbc, 0x99, 0x9d, 0xf7, + 0x66, 0x0c, 0xa7, 0x39, 0x33, 0xd3, 0x32, 0x19, 0xa6, 0x82, 0x87, 0x7c, 0x94, 0x25, 0x21, 0x1f, + 0x85, 0x5a, 0xa5, 0xe1, 0x75, 0x89, 0xea, 0x26, 0xcc, 0xb1, 0x40, 0x45, 0x0d, 0x66, 0xa1, 0x54, + 0xc2, 0x88, 0x90, 0x66, 0x9c, 0x15, 0x61, 0x46, 0x0d, 0x4d, 0xa8, 0xc6, 0xa1, 0x03, 0x49, 0xc7, + 0xa1, 0xc7, 0xe3, 0x5b, 0x74, 0x2a, 0x28, 0x47, 0x2d, 0x69, 0x5a, 0xb5, 0xba, 0x55, 0x0f, 0x39, + 0xa3, 0x29, 0x72, 0x2c, 0xcc, 0xb2, 0x47, 0xff, 0x77, 0x0b, 0x1e, 0x9e, 0x55, 0x13, 0xbe, 0x56, + 0x48, 0x0d, 0x46, 0x78, 0x5d, 0xa2, 0x36, 0xe4, 0x39, 0xec, 0x37, 0x0f, 0xc6, 0x36, 0x0a, 0xbc, + 0x9e, 0x37, 0xf0, 0xa3, 0xbd, 0x06, 0xbd, 0xa4, 0x1c, 0x09, 0x81, 0x2d, 0x73, 0x23, 0x31, 0x68, + 0xb9, 0xa4, 0x8b, 0xc9, 0x63, 0x80, 0xa2, 0xe4, 0xb1, 0x9e, 0x52, 0x95, 0xe9, 0xa0, 0xdd, 0xf3, + 0x06, 0x9d, 0xc8, 0x2f, 0x4a, 0x3e, 0x71, 0x00, 0x79, 0x09, 0x44, 0xa1, 0x9c, 0xb1, 0x94, 0x1a, + 0x26, 0x8a, 0xf8, 0x03, 0x4d, 0x8d, 0x50, 0xc1, 0x96, 0xa3, 0xdd, 0x5f, 0xcb, 0xbc, 0x71, 0x09, + 0x3b, 0x88, 0x42, 0x83, 0x85, 0x23, 0x1b, 0xc6, 0x31, 0xe8, 0x2c, 0x07, 0x69, 0xd0, 0xf7, 0x8c, + 0x23, 0x09, 0x01, 0x92, 0x99, 0x48, 0x3f, 0xc6, 0x9a, 0xcd, 0x31, 0xd8, 0xee, 0x79, 0x83, 0xdd, + 0x93, 0x83, 0xa1, 0x53, 0x3d, 0x1c, 0xdb, 0xc4, 0x84, 0xcd, 0x31, 0xf2, 0x93, 0x3a, 0x24, 0x4f, + 0xa1, 0x33, 0x15, 0xda, 0xe8, 0x60, 0xa7, 0xd7, 0x1e, 0xec, 0x9e, 0xec, 0x56, 0xdc, 0x0b, 0xa1, + 0x4d, 0xb4, 0xcc, 0x90, 0x77, 0xf0, 0x80, 0xe6, 0xb9, 0xc2, 0xdc, 0xfa, 0x18, 0x37, 0xc2, 0x83, + 0xbb, 0xae, 0xfb, 0x71, 0x55, 0x71, 0xda, 0x50, 0x2e, 0x6b, 0x46, 0x74, 0x48, 0xff, 0x07, 0xfb, + 0x12, 0x0e, 0x37, 0x70, 0xad, 0x85, 0x6b, 0xfe, 0xba, 0x98, 0x74, 0x01, 0x14, 0x6a, 0x31, 0x2b, + 0xad, 0xbe, 0xca, 0xdc, 0x35, 0x64, 0x83, 0x29, 0xed, 0x0d, 0xa6, 0xf4, 0x39, 0xf8, 0x8d, 0x76, + 0xb7, 0x2a, 0xb6, 0x7a, 0xc7, 0xc6, 0xe4, 0x2d, 0x3c, 0xc3, 0xcf, 0x12, 0x53, 0xab, 0x4f, 0xa3, + 0x62, 0xa8, 0x63, 0x7b, 0xb0, 0x52, 0xb0, 0xc2, 0xe8, 0x58, 0xa2, 0x8a, 0xa7, 0xa2, 0x54, 0x6e, + 0x80, 0x76, 0xf4, 0xa4, 0xa6, 0x4e, 0x1c, 0xf3, 0xac, 0x21, 0x5e, 0xa1, 0xba, 0x10, 0xa5, 0xea, + 0x7f, 0xf3, 0x60, 0xcb, 0xfa, 0x47, 0xf6, 0xa1, 0xc5, 0xb2, 0xea, 0xa1, 0x16, 0xcb, 0x48, 0x00, + 0x3b, 0x34, 0xcb, 0x14, 0x6a, 0x5d, 0x69, 0xa9, 0x3f, 0xed, 0x50, 0x52, 0x28, 0xe3, 0xc6, 0xdf, + 0x8b, 0x5c, 0x4c, 0x5e, 0xc0, 0x3d, 0xa6, 0xc5, 0x6c, 0x79, 0x1e, 0xb9, 0x12, 0xa5, 0x74, 0xd7, + 0xe1, 0x47, 0xfb, 0x0d, 0x7c, 0x6e, 0x51, 0x5b, 0x3c, 0x17, 0x45, 0x7d, 0x10, 0x2e, 0x26, 0x47, + 0xb0, 0xfd, 0x09, 0x59, 0x3e, 0x35, 0xee, 0x06, 0xf6, 0xa2, 0xea, 0xab, 0xff, 0xc5, 0x83, 0xa3, + 0x7f, 0x2f, 0x5d, 0x4b, 0x51, 0x68, 0x24, 0xaf, 0xc0, 0x5f, 0xed, 0xd6, 0x73, 0xbb, 0x7d, 0x54, + 0xed, 0xb6, 0xd9, 0xd2, 0x39, 0x9a, 0x9a, 0x1f, 0xad, 0xd8, 0xb6, 0xb4, 0xf9, 0xa5, 0x9c, 0xb4, + 0x55, 0xe9, 0x55, 0x8d, 0xff, 0x55, 0xda, 0xb0, 0xc7, 0x07, 0xdf, 0x17, 0x5d, 0xef, 0xc7, 0xa2, + 0xeb, 0xfd, 0x5c, 0x74, 0xbd, 0xaf, 0xbf, 0xba, 0x77, 0x92, 0x6d, 0xf7, 0x4f, 0x8e, 0xfe, 0x04, + 0x00, 0x00, 0xff, 0xff, 0xce, 0xeb, 0x60, 0x31, 0x67, 0x04, 0x00, 0x00, } diff --git a/src/query/generated/proto/admin/database.proto b/src/query/generated/proto/admin/database.proto index 7766bbc935..ab64453739 100644 --- a/src/query/generated/proto/admin/database.proto +++ b/src/query/generated/proto/admin/database.proto @@ -23,6 +23,22 @@ message DatabaseCreateRequest { // Required if not using local database type repeated Host hosts = 7; + + // Optional field to add an additional aggregated namespace. + AggregatedNamespace aggregated_namespace = 8; +} + +message AggregatedNamespace { + // Required fields. + // Namespace name. + string name = 1; + + // Time window to aggregate data points across. + // Uses same shorthand as retention_time. + string resolution = 2; + + // Length of time to retain data. + string retention_time = 3; } message BlockSize {