From f4bdbc6e6b6d257603d1c31996bd18ec5e55af9f Mon Sep 17 00:00:00 2001 From: "denis.bilenko" Date: Tue, 3 Nov 2026 12:13:14 +0000 Subject: [PATCH] acc: update postgres update_mask=spec factcheck for new backend behavior Since 2026-11-02 the postgres API no longer returns 500 for update_mask=spec with a body that omits non-default default_endpoint_settings; it keeps the stored settings. Drop the 500 emulation from the testserver and update the golden. Co-authored-by: Isaac --- .../postgres_projects/factcheck/test.toml | 6 --- .../factcheck/update_mask_spec/output.txt | 24 +++++++-- .../factcheck/update_mask_spec/script | 5 +- libs/testserver/postgres.go | 49 +------------------ 4 files changed, 25 insertions(+), 59 deletions(-) diff --git a/acceptance/bundle/resources/postgres_projects/factcheck/test.toml b/acceptance/bundle/resources/postgres_projects/factcheck/test.toml index 7bcd836ad30..43404da5e4b 100644 --- a/acceptance/bundle/resources/postgres_projects/factcheck/test.toml +++ b/acceptance/bundle/resources/postgres_projects/factcheck/test.toml @@ -8,9 +8,3 @@ EnvMatrix.DMS = [] Old = "Field 'spec\\.[a-z_.]+' is in update_mask but not provided" New = "Field '[SPEC_FIELD]' is in update_mask but not provided" Order = 1 - -[[Repls]] -# The whole-spec 500 carries a per-request trace id. -Old = '\[TraceId: [0-9a-f]+\]' -New = '[TraceId: [TRACE_ID]]' -Order = 1 diff --git a/acceptance/bundle/resources/postgres_projects/factcheck/update_mask_spec/output.txt b/acceptance/bundle/resources/postgres_projects/factcheck/update_mask_spec/output.txt index 315e46f8061..d4ac98d0a77 100644 --- a/acceptance/bundle/resources/postgres_projects/factcheck/update_mask_spec/output.txt +++ b/acceptance/bundle/resources/postgres_projects/factcheck/update_mask_spec/output.txt @@ -106,9 +106,27 @@ === update_mask=spec with a body that now omits the non-default default_endpoint_settings >>> [CLI] postgres update-project projects/factcheck-spec-[UNIQUE_NAME] spec --json {"spec":{"display_name":"changed2","pg_version":16,"history_retention_duration":"604800s"}} -Error: The request could not be processed. Please try again later and contact Databricks if the problem persists. [TraceId: [TRACE_ID]] - -Exit code: 1 +{ + "name": "projects/factcheck-spec-[UNIQUE_NAME]", + "project_id": "factcheck-spec-[UNIQUE_NAME]", + "status": { + "branch_logical_size_limit_bytes": [BRANCH_SIZE_LIMIT], + "default_branch": "projects/factcheck-spec-[UNIQUE_NAME]/branches/production", + "default_endpoint_settings": { + "autoscaling_limit_max_cu": 4, + "autoscaling_limit_min_cu": 0.5, + "suspend_timeout_duration": "300s" + }, + "display_name": "changed2", + "enable_pg_native_login": false, + "history_retention_duration": "604800s", + "owner": "[USERNAME]", + "pg_version": 16, + "project_id": "factcheck-spec-[UNIQUE_NAME]", + "synthetic_storage_size_bytes": 0 + }, + "uid": "[UUID]" +} >>> print_requests.py //postgres/projects --method PATCH { diff --git a/acceptance/bundle/resources/postgres_projects/factcheck/update_mask_spec/script b/acceptance/bundle/resources/postgres_projects/factcheck/update_mask_spec/script index a31e1f57ccd..5efc6703f2a 100644 --- a/acceptance/bundle/resources/postgres_projects/factcheck/update_mask_spec/script +++ b/acceptance/bundle/resources/postgres_projects/factcheck/update_mask_spec/script @@ -36,9 +36,8 @@ trace $CLI postgres get-project "$name" | project_fields trace print_requests.py //postgres/projects --method PATCH title "update_mask=spec with a body that now omits the non-default default_endpoint_settings" -# 500: masking the whole spec resets the omitted sub-message and its emptied autoscaling -# limits fail validation. The other fields are applied first, so it is a partial write — -# the final read below shows display_name changed to changed2 while the response errored. +# Masking the whole spec keeps the omitted sub-message: the stored endpoint settings +# survive. (Until 2026-11-02 the API returned 500 here, after a partial write.) errcode trace $CLI postgres update-project "$name" spec --json '{"spec":{"display_name":"changed2","pg_version":16,"history_retention_duration":"604800s"}}' | project_fields trace print_requests.py //postgres/projects --method PATCH trace $CLI postgres get-project "$name" | project_fields diff --git a/libs/testserver/postgres.go b/libs/testserver/postgres.go index 2f59389aeb1..9d9e71b5d98 100644 --- a/libs/testserver/postgres.go +++ b/libs/testserver/postgres.go @@ -1,8 +1,6 @@ package testserver import ( - "crypto/rand" - "encoding/hex" "encoding/json" "fmt" "regexp" @@ -19,14 +17,6 @@ func nowTime() *sdktime.Time { return sdktime.New(time.Now().UTC()) } -// nextTraceID returns a 32-hex-character id shaped like the trace ids the real API puts -// in error messages, so acceptance goldens normalize local and cloud identically. -func nextTraceID() string { - b := make([]byte, 16) - _, _ = rand.Read(b) - return hex.EncodeToString(b) -} - // Backend defaults applied to a project's status when the spec omits them. // These mirror the values the real API materializes on create. const ( @@ -261,20 +251,8 @@ func (s *FakeWorkspace) PostgresProjectUpdate(req Request, name string) Response project.UpdateTime = nowTime() s.PostgresProjects[name] = project - // Whole-spec mask + a body that omits default_endpoint_settings, while the stored - // settings are non-default, makes the real API 500 ("could not be processed"): it - // resets the message and the emptied autoscaling limits fail validation. The other - // masked fields are applied first, though — the failure is a partial write, so the - // mutation above is persisted and only this response reports the error. When the - // settings are already default, or the message is masked by its own path, the - // omission is tolerated. Probed against a real workspace 2026-09-21. - if wholeSpecMasked(req.URL.Query().Get("update_mask")) && - (updateProject.Spec == nil || updateProject.Spec.DefaultEndpointSettings == nil) && - project.Status != nil && isNonDefaultEndpointSettings(project.Status.DefaultEndpointSettings) { - return postgresErrorResponse(500, "INTERNAL_ERROR", - fmt.Sprintf("The request could not be processed. Please try again later and contact Databricks if the problem persists. [TraceId: %s]", nextTraceID())) - } - + // Whole-spec mask + a body that omits default_endpoint_settings keeps the stored + // settings. Until 2026-11-02 the real API returned 500 here when they were non-default. return Response{ Body: s.createOperationLocked(project.Name, project), } @@ -997,29 +975,6 @@ func validateUpdateMask(req Request, allowed []string, oneofGroups map[string][] return nil } -// wholeSpecMasked reports whether the update_mask names the whole spec message (a bare -// "spec" path), as the Terraform provider does. -func wholeSpecMasked(mask string) bool { - for path := range strings.SplitSeq(mask, ",") { - if strings.TrimSpace(path) == "spec" { - return true - } - } - return false -} - -// isNonDefaultEndpointSettings reports whether the settings differ from what create -// materializes by default (autoscaling 1..1, 86400s suspend). -func isNonDefaultEndpointSettings(s *postgres.ProjectDefaultEndpointSettings) bool { - if s == nil { - return false - } - if s.AutoscalingLimitMinCu != defaultAutoscalingLimitMinCu || s.AutoscalingLimitMaxCu != defaultAutoscalingLimitMaxCu { - return true - } - return s.SuspendTimeoutDuration != nil && s.SuspendTimeoutDuration.AsDuration() != defaultSuspendTimeout -} - // wildcardMissingField returns the first spec field that update_mask=* requires but the // body does not carry. The wildcard expands to every settable leaf, and — unlike a bare // message mask — the API then demands each one be present (probed against a real