Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

avoid backend_quota edit war for AZSeparatedResourceTopology #635

Merged
merged 2 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion internal/collector/scrape.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,9 @@ func (c *Collector) writeResourceScrapeResult(dbDomain db.Domain, dbProject db.P

resInfo := c.Cluster.InfoForResource(srv.Type, res.Name)
if resInfo.HasQuota {
res.BackendQuota = &backendQuota
if resInfo.Topology != liquid.AZSeparatedResourceTopology {
res.BackendQuota = &backendQuota
}
res.MinQuotaFromBackend = resourceData[res.Name].MinQuota
res.MaxQuotaFromBackend = resourceData[res.Name].MaxQuota
}
Expand Down Expand Up @@ -438,6 +440,10 @@ func (c *Collector) writeDummyResources(dbDomain db.Domain, dbProject db.Project
}
}

// FIXME: These dummy resources do not conform to `resInfo.Topology` and are never AZ-aware.
// I'm not fixing this right now because dummy resources are an extremely rare corner-case anyway.
// TODO: When we rework the DB schema next year, we should build it so that dummy resources can be avoided entirely.

// update scraped_at timestamp and reset stale flag to make sure that we do
// not scrape this service again immediately afterwards if there are other
// stale services to cover first
Expand Down
32 changes: 11 additions & 21 deletions internal/collector/scrape_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -622,35 +622,29 @@ func Test_TopologyScrapes(t *testing.T) {
INSERT INTO project_az_resources (id, resource_id, az, usage, subresources, historical_usage, backend_quota) VALUES (7, 3, 'az-two', 2, '[{"index":2},{"index":3}]', '{"t":[%[1]d],"v":[2]}', 21);
INSERT INTO project_az_resources (id, resource_id, az, usage, physical_usage, historical_usage, backend_quota) VALUES (8, 4, 'az-one', 0, 0, '{"t":[%[3]d],"v":[0]}', 50);
INSERT INTO project_az_resources (id, resource_id, az, usage, physical_usage, historical_usage, backend_quota) VALUES (9, 4, 'az-two', 0, 0, '{"t":[%[3]d],"v":[0]}', 50);
INSERT INTO project_resources (id, service_id, name, quota, backend_quota) VALUES (1, 1, 'capacity', 0, 100);
INSERT INTO project_resources (id, service_id, name) VALUES (1, 1, 'capacity');
INSERT INTO project_resources (id, service_id, name) VALUES (2, 1, 'capacity_portion');
INSERT INTO project_resources (id, service_id, name, quota, backend_quota) VALUES (3, 1, 'things', 0, 42);
INSERT INTO project_resources (id, service_id, name, quota, backend_quota) VALUES (4, 2, 'capacity', 0, 100);
INSERT INTO project_resources (id, service_id, name) VALUES (3, 1, 'things');
INSERT INTO project_resources (id, service_id, name) VALUES (4, 2, 'capacity');
INSERT INTO project_resources (id, service_id, name) VALUES (5, 2, 'capacity_portion');
INSERT INTO project_resources (id, service_id, name, quota, backend_quota) VALUES (6, 2, 'things', 0, 42);
UPDATE project_services SET scraped_at = %[1]d, scrape_duration_secs = 5, serialized_metrics = '{"capacity_usage":0,"things_usage":4}', checked_at = %[1]d, next_scrape_at = %[2]d, quota_desynced_at = %[1]d WHERE id = 1 AND project_id = 1 AND type = 'unittest';
UPDATE project_services SET scraped_at = %[3]d, scrape_duration_secs = 5, serialized_metrics = '{"capacity_usage":0,"things_usage":4}', checked_at = %[3]d, next_scrape_at = %[4]d, quota_desynced_at = %[3]d WHERE id = 2 AND project_id = 2 AND type = 'unittest';
INSERT INTO project_resources (id, service_id, name) VALUES (6, 2, 'things');
UPDATE project_services SET scraped_at = %[1]d, scrape_duration_secs = 5, serialized_metrics = '{"capacity_usage":0,"things_usage":4}', checked_at = %[1]d, next_scrape_at = %[2]d WHERE id = 1 AND project_id = 1 AND type = 'unittest';
UPDATE project_services SET scraped_at = %[3]d, scrape_duration_secs = 5, serialized_metrics = '{"capacity_usage":0,"things_usage":4}', checked_at = %[3]d, next_scrape_at = %[4]d WHERE id = 2 AND project_id = 2 AND type = 'unittest';
`,
scrapedAt1.Unix(), scrapedAt1.Add(scrapeInterval).Unix(),
scrapedAt2.Unix(), scrapedAt2.Add(scrapeInterval).Unix(),
)

// set some quota acpq values.
// resource level (ACPQ always writes NULL on this level for AZSeparatedResourceTopology)
_, err := s.DB.Exec(`UPDATE project_resources SET quota = NULL WHERE name = $1`, "capacity")
_, err := s.DB.Exec(`UPDATE project_az_resources SET quota = $1 WHERE resource_id IN (1,4) and az != 'any'`, 20)
if err != nil {
t.Fatal(err)
}
_, err = s.DB.Exec(`UPDATE project_resources SET quota = NULL WHERE name = $1`, "things")
if err != nil {
t.Fatal(err)
}
// az level
_, err = s.DB.Exec(`UPDATE project_az_resources SET quota = $1 WHERE resource_id IN (1,4) and az != 'any'`, 20)
_, err = s.DB.Exec(`UPDATE project_az_resources SET quota = $1 WHERE resource_id IN (3,6) and az != 'any'`, 13)
if err != nil {
t.Fatal(err)
}
_, err = s.DB.Exec(`UPDATE project_az_resources SET quota = $1 WHERE resource_id IN (3,6) and az != 'any'`, 13)
_, err = s.DB.Exec(`UPDATE project_services SET quota_desynced_at = $1`, s.Clock.Now())
if err != nil {
t.Fatal(err)
}
Expand All @@ -668,10 +662,6 @@ func Test_TopologyScrapes(t *testing.T) {
UPDATE project_az_resources SET backend_quota = 13 WHERE id = 7 AND resource_id = 3 AND az = 'az-two';
UPDATE project_az_resources SET backend_quota = 20 WHERE id = 8 AND resource_id = 4 AND az = 'az-one';
UPDATE project_az_resources SET backend_quota = 20 WHERE id = 9 AND resource_id = 4 AND az = 'az-two';
UPDATE project_resources SET backend_quota = NULL WHERE id = 1 AND service_id = 1 AND name = 'capacity';
UPDATE project_resources SET backend_quota = NULL WHERE id = 3 AND service_id = 1 AND name = 'things';
UPDATE project_resources SET backend_quota = NULL WHERE id = 4 AND service_id = 2 AND name = 'capacity';
UPDATE project_resources SET backend_quota = NULL WHERE id = 6 AND service_id = 2 AND name = 'things';
UPDATE project_services SET quota_desynced_at = NULL, quota_sync_duration_secs = 5 WHERE id = 1 AND project_id = 1 AND type = 'unittest';
UPDATE project_services SET quota_desynced_at = NULL, quota_sync_duration_secs = 5 WHERE id = 2 AND project_id = 2 AND type = 'unittest';
`)
Expand All @@ -696,9 +686,7 @@ func Test_TopologyScrapes(t *testing.T) {
UPDATE project_az_resources SET backend_quota = NULL WHERE id = 7 AND resource_id = 3 AND az = 'az-two';
UPDATE project_az_resources SET backend_quota = 50 WHERE id = 8 AND resource_id = 4 AND az = 'az-one';
UPDATE project_az_resources SET backend_quota = 50 WHERE id = 9 AND resource_id = 4 AND az = 'az-two';
UPDATE project_resources SET quota = 0, backend_quota = 40 WHERE id = 1 AND service_id = 1 AND name = 'capacity';
UPDATE project_resources SET quota = 0, backend_quota = 26 WHERE id = 3 AND service_id = 1 AND name = 'things';
UPDATE project_resources SET quota = 0, backend_quota = 40 WHERE id = 4 AND service_id = 2 AND name = 'capacity';
UPDATE project_resources SET quota = 0, backend_quota = 26 WHERE id = 6 AND service_id = 2 AND name = 'things';
UPDATE project_services SET scraped_at = %[1]d, checked_at = %[1]d, next_scrape_at = %[2]d, quota_desynced_at = %[1]d WHERE id = 1 AND project_id = 1 AND type = 'unittest';
UPDATE project_services SET scraped_at = %[3]d, checked_at = %[3]d, next_scrape_at = %[4]d, quota_desynced_at = %[3]d WHERE id = 2 AND project_id = 2 AND type = 'unittest';
Expand All @@ -724,6 +712,8 @@ func Test_TopologyScrapes(t *testing.T) {
DELETE FROM project_az_resources WHERE id = 16 AND resource_id = 6 AND az = 'any';
UPDATE project_az_resources SET backend_quota = 21 WHERE id = 6 AND resource_id = 3 AND az = 'az-one';
UPDATE project_az_resources SET usage = 0, subresources = '', historical_usage = '{"t":[%[1]d,%[3]d],"v":[2,0]}' WHERE id = 7 AND resource_id = 3 AND az = 'az-two';
UPDATE project_resources SET quota = NULL, backend_quota = NULL WHERE id = 3 AND service_id = 1 AND name = 'things';
UPDATE project_resources SET quota = NULL, backend_quota = NULL WHERE id = 6 AND service_id = 2 AND name = 'things';
UPDATE project_services SET scraped_at = %[3]d, serialized_metrics = '{"capacity_usage":0,"things_usage":2}', checked_at = %[3]d, next_scrape_at = %[4]d WHERE id = 1 AND project_id = 1 AND type = 'unittest';
UPDATE project_services SET scraped_at = %[5]d, serialized_metrics = '{"capacity_usage":0,"things_usage":2}', checked_at = %[5]d, next_scrape_at = %[6]d WHERE id = 2 AND project_id = 2 AND type = 'unittest';
`,
Expand Down
4 changes: 2 additions & 2 deletions internal/datamodel/project_resource_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func (u ProjectResourceUpdate) Run(dbi db.Interface, cluster *core.Cluster, now
result = append(result, res)

// check if we need to arrange for SetQuotaJob to look at this project service
if resInfo.HasQuota {
if resInfo.HasQuota && resInfo.Topology != liquid.AZSeparatedResourceTopology {
backendQuota := unwrapOrDefault(res.BackendQuota, -1)
quota := *res.Quota // definitely not nil, it was set above in validateResourceConstraints()
if backendQuota < 0 || uint64(backendQuota) != quota {
Expand Down Expand Up @@ -172,7 +172,7 @@ func unwrapOrDefault[T any](value *T, defaultValue T) T {

// Ensures that `res` conforms to various constraints and validation rules.
func validateResourceConstraints(res *db.ProjectResource, resInfo liquid.ResourceInfo) {
if !resInfo.HasQuota {
if !resInfo.HasQuota || resInfo.Topology == liquid.AZSeparatedResourceTopology {
// ensure that NoQuota resources do not contain any quota values
res.Quota = nil
res.BackendQuota = nil
Expand Down
8 changes: 6 additions & 2 deletions internal/test/plugins/quota_generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,12 @@ func (p *GenericQuotaPlugin) Scrape(ctx context.Context, project core.KeystonePr
}

// populate azSeparatedQuota
for az, data := range copyOfVal.UsageData {
data.Quota = val.UsageData[az].Quota
topology := p.LiquidServiceInfo.Resources[key].Topology
if topology == liquid.AZSeparatedResourceTopology {
copyOfVal.Quota = 0
for az, data := range copyOfVal.UsageData {
data.Quota = val.UsageData[az].Quota
}
}

// test coverage for PhysicalUsage != Usage
Expand Down
Loading