feat(store): globally unique subdomains and an insert that surfaces conflicts

Once an endpoint hangs off a shared zone rather than a per-cluster address,
subdomain labels must be unique across the whole zone rather than within one
cluster. Uniqueness was previously advisory -- a pre-read "taken" set with no
database constraint -- so this adds a unique index on the column and makes
the database the arbiter.

CreateAgentNetworkSettings is a plain INSERT that returns the driver error
unwrapped, both of which the allocator depends on: SaveAgentNetworkSettings
is an upsert (which cannot conflict) and wraps failures in a generic internal
error, discarding the message that unique-violation detection needs.

Note for operators: the index is created by a migration that fails, and
therefore blocks startup, on a deployment that already holds two rows with
the same subdomain on different clusters -- which was legal under the old
per-cluster scheme. Audit for duplicates before upgrading.
This commit is contained in:
Brad Ison
2026-08-03 23:36:04 +02:00
parent df3619e1e5
commit fabfacee55
4 changed files with 123 additions and 0 deletions
@@ -346,6 +346,21 @@ func (s *SqlStore) SaveAgentNetworkSettings(ctx context.Context, settings *agent
return nil
}
// CreateAgentNetworkSettings inserts a new settings row.
//
// Unlike SaveAgentNetworkSettings (an upsert) this is a plain INSERT, and it
// returns the driver error unwrapped. Both properties are required by the
// subdomain allocator: it relies on the unique index rejecting a duplicate
// label, and on being able to recognise that rejection so it can retry with a
// fresh label instead of surfacing an error.
func (s *SqlStore) CreateAgentNetworkSettings(ctx context.Context, settings *agentNetworkTypes.Settings) error {
if err := s.db.Create(settings).Error; err != nil {
log.WithContext(ctx).Debugf("failed to create agent network settings: %v", err)
return err
}
return nil
}
// IncrementAgentNetworkConsumption atomically upserts the consumption
// row keyed on (account, dim_kind, dim_id, window_seconds, window_start)
// and adds the supplied deltas. Concurrent calls from multiple proxy