mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-18 14:40:26 +01:00
44b071296d
### Describe Your Changes Added an ability to query data across multiple tenants. See: https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1434 Currently, the following endpoints work with multi-tenancy: - /prometheus/api/v1/query - /prometheus/api/v1/query_range - /prometheus/api/v1/series - /prometheus/api/v1/labels - /prometheus/api/v1/label/<label_name>/values - /prometheus/api/v1/status/active_queries - /prometheus/api/v1/status/top_queries - /prometheus/api/v1/status/tsdb - /prometheus/api/v1/export - /prometheus/api/v1/export/csv - /vmui A note regarding VMUI: endpoints such as `active_queries` and `top_queries` have been updated to indicate whether query was a single-tenant or multi-tenant, but UI needs to be updated to display this info. cc: @Loori-R --------- Signed-off-by: Zakhar Bessarab <z.bessarab@victoriametrics.com> Signed-off-by: f41gh7 <nik@victoriametrics.com> Co-authored-by: f41gh7 <nik@victoriametrics.com>
92 lines
4.3 KiB
Go
92 lines
4.3 KiB
Go
package netstorage
|
|
|
|
import (
|
|
"reflect"
|
|
"sort"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
|
)
|
|
|
|
func TestFetchingTenants(t *testing.T) {
|
|
tc := newTenantsCache(5 * time.Second)
|
|
|
|
dayMs := (time.Hour * 24 * 1000).Milliseconds()
|
|
|
|
tc.put(storage.TimeRange{MinTimestamp: 0, MaxTimestamp: 0}, []storage.TenantToken{
|
|
{AccountID: 1, ProjectID: 1},
|
|
{AccountID: 1, ProjectID: 0},
|
|
})
|
|
tc.put(storage.TimeRange{MinTimestamp: 0, MaxTimestamp: dayMs - 1}, []storage.TenantToken{
|
|
{AccountID: 1, ProjectID: 1},
|
|
{AccountID: 1, ProjectID: 0},
|
|
})
|
|
tc.put(storage.TimeRange{MinTimestamp: dayMs, MaxTimestamp: 2*dayMs - 1}, []storage.TenantToken{
|
|
{AccountID: 2, ProjectID: 1},
|
|
{AccountID: 2, ProjectID: 0},
|
|
})
|
|
tc.put(storage.TimeRange{MinTimestamp: 2 * dayMs, MaxTimestamp: 3*dayMs - 1}, []storage.TenantToken{
|
|
{AccountID: 3, ProjectID: 1},
|
|
{AccountID: 3, ProjectID: 0},
|
|
})
|
|
|
|
f := func(tr storage.TimeRange, expectedTenants []storage.TenantToken) {
|
|
t.Helper()
|
|
tenants := tc.get(tr)
|
|
|
|
if len(tenants) == 0 && len(tenants) == len(expectedTenants) {
|
|
return
|
|
}
|
|
|
|
sortTenants := func(t []storage.TenantToken) func(i, j int) bool {
|
|
return func(i, j int) bool {
|
|
if t[i].AccountID == t[j].AccountID {
|
|
return t[i].ProjectID < t[j].ProjectID
|
|
}
|
|
return t[i].AccountID < t[j].AccountID
|
|
}
|
|
}
|
|
sort.Slice(tenants, sortTenants(tenants))
|
|
sort.Slice(expectedTenants, sortTenants(expectedTenants))
|
|
|
|
if !reflect.DeepEqual(tenants, expectedTenants) {
|
|
t.Fatalf("unexpected tenants; got %v; want %v", tenants, expectedTenants)
|
|
}
|
|
}
|
|
|
|
// Basic time range coverage
|
|
f(storage.TimeRange{MinTimestamp: 0, MaxTimestamp: 0}, []storage.TenantToken{{AccountID: 1, ProjectID: 1}, {AccountID: 1, ProjectID: 0}})
|
|
f(storage.TimeRange{MinTimestamp: 0, MaxTimestamp: 100}, []storage.TenantToken{{AccountID: 1, ProjectID: 1}, {AccountID: 1, ProjectID: 0}})
|
|
f(storage.TimeRange{MinTimestamp: dayMs, MaxTimestamp: dayMs}, []storage.TenantToken{{AccountID: 2, ProjectID: 1}, {AccountID: 2, ProjectID: 0}})
|
|
f(storage.TimeRange{MinTimestamp: 2 * dayMs, MaxTimestamp: 2 * dayMs}, []storage.TenantToken{{AccountID: 3, ProjectID: 1}, {AccountID: 3, ProjectID: 0}})
|
|
f(storage.TimeRange{MinTimestamp: 3 * dayMs, MaxTimestamp: 3*dayMs + 1}, []storage.TenantToken{})
|
|
|
|
// Time range inside existing range
|
|
f(storage.TimeRange{MinTimestamp: dayMs / 2, MaxTimestamp: dayMs/2 + 100}, []storage.TenantToken{{AccountID: 1, ProjectID: 1}, {AccountID: 1, ProjectID: 0}})
|
|
f(storage.TimeRange{MinTimestamp: dayMs + dayMs/2, MaxTimestamp: dayMs + dayMs/2 + 100}, []storage.TenantToken{{AccountID: 2, ProjectID: 1}, {AccountID: 2, ProjectID: 0}})
|
|
f(storage.TimeRange{MinTimestamp: 0, MaxTimestamp: dayMs / 2}, []storage.TenantToken{{AccountID: 1, ProjectID: 1}, {AccountID: 1, ProjectID: 0}})
|
|
f(storage.TimeRange{MinTimestamp: dayMs / 2, MaxTimestamp: dayMs - 1}, []storage.TenantToken{{AccountID: 1, ProjectID: 1}, {AccountID: 1, ProjectID: 0}})
|
|
|
|
// Overlapping time ranges
|
|
f(storage.TimeRange{MinTimestamp: 0, MaxTimestamp: 2*dayMs - 1}, []storage.TenantToken{{AccountID: 1, ProjectID: 1}, {AccountID: 1, ProjectID: 0}, {AccountID: 2, ProjectID: 1}, {AccountID: 2, ProjectID: 0}})
|
|
f(storage.TimeRange{MinTimestamp: dayMs / 2, MaxTimestamp: dayMs + dayMs/2}, []storage.TenantToken{{AccountID: 1, ProjectID: 1}, {AccountID: 1, ProjectID: 0}, {AccountID: 2, ProjectID: 1}, {AccountID: 2, ProjectID: 0}})
|
|
}
|
|
|
|
func TestHasIntersection(t *testing.T) {
|
|
f := func(inner, outer storage.TimeRange, expected bool) {
|
|
t.Helper()
|
|
if hasIntersection(inner, outer) != expected {
|
|
t.Fatalf("unexpected result for inner=%+v, outer=%+v", inner, outer)
|
|
}
|
|
}
|
|
|
|
f(storage.TimeRange{MinTimestamp: 0, MaxTimestamp: 150}, storage.TimeRange{MinTimestamp: 0, MaxTimestamp: 0}, true)
|
|
f(storage.TimeRange{MinTimestamp: 0, MaxTimestamp: 150}, storage.TimeRange{MinTimestamp: 0, MaxTimestamp: 100}, true)
|
|
f(storage.TimeRange{MinTimestamp: 50, MaxTimestamp: 150}, storage.TimeRange{MinTimestamp: 0, MaxTimestamp: 100}, true)
|
|
f(storage.TimeRange{MinTimestamp: 50, MaxTimestamp: 150}, storage.TimeRange{MinTimestamp: 10, MaxTimestamp: 80}, true)
|
|
|
|
f(storage.TimeRange{MinTimestamp: 0, MaxTimestamp: 50}, storage.TimeRange{MinTimestamp: 60, MaxTimestamp: 100}, false)
|
|
f(storage.TimeRange{MinTimestamp: 100, MaxTimestamp: 150}, storage.TimeRange{MinTimestamp: 60, MaxTimestamp: 80}, false)
|
|
}
|