-
Notifications
You must be signed in to change notification settings - Fork 6
[after GA] Commitments syncer and change-api share mutex lock #640
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,31 @@ type UsageNovaClient interface { | |
| ListProjectServers(ctx context.Context, projectID string) ([]nova.ServerDetail, error) | ||
| } | ||
|
|
||
| // CRMutex serializes CR state changes between the syncer and change-commitments API. | ||
| // This ensures that the syncer's Limes state snapshot is applied atomically without | ||
| // interference from concurrent change-commitments API calls. The Lock and Unlock | ||
| // methods are no-ops if the receiver is nil, allowing safe use when either component | ||
| // is disabled. | ||
| // TODO: If the syncer and API are moved to separate pods, replace with a K8s | ||
| // distributed lock (e.g., Lease-based coordination). | ||
| type CRMutex struct { | ||
| mu sync.Mutex | ||
| } | ||
|
|
||
| // Lock acquires the mutex. No-op if receiver is nil. | ||
| func (m *CRMutex) Lock() { | ||
| if m != nil { | ||
| m.mu.Lock() | ||
| } | ||
| } | ||
|
|
||
| // Unlock releases the mutex. No-op if receiver is nil. | ||
| func (m *CRMutex) Unlock() { | ||
| if m != nil { | ||
| m.mu.Unlock() | ||
| } | ||
| } | ||
|
|
||
| // HTTPAPI implements Limes LIQUID commitment validation endpoints. | ||
| type HTTPAPI struct { | ||
| client client.Client | ||
|
|
@@ -30,15 +55,19 @@ type HTTPAPI struct { | |
| usageMonitor ReportUsageAPIMonitor | ||
| capacityMonitor ReportCapacityAPIMonitor | ||
| infoMonitor InfoAPIMonitor | ||
| // Mutex to serialize change-commitments requests | ||
| changeMutex sync.Mutex | ||
| // Shared mutex to serialize CR state changes with the syncer | ||
| crMutex *CRMutex | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The ideomatic way in Golang is as follows. You can use sync.Mutex as a field in your type struct. type HTTPAPI struct {
mutex sync.Mutex
}And when you're initializing the struct, you can directly use your mutex. No nil check needed. api := HTTPAPI{}
api.mutex.Lock() // This should work without explicit initialization
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This because you are embedding mutex here, and initializing the surrounding struct will initialize the embedded struct as well.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The mutex wrapper shouldn't be necessary. |
||
| } | ||
|
|
||
| func NewAPI(client client.Client) *HTTPAPI { | ||
| return NewAPIWithConfig(client, DefaultConfig(), nil) | ||
| return NewAPIWithConfig(client, DefaultConfig(), nil, nil) | ||
| } | ||
|
|
||
| func NewAPIWithConfig(client client.Client, config Config, novaClient UsageNovaClient) *HTTPAPI { | ||
| func NewAPIWithConfig(client client.Client, config Config, novaClient UsageNovaClient, crMutex *CRMutex) *HTTPAPI { | ||
| // If no shared mutex provided, create a local one (for backwards compatibility in tests) | ||
| if crMutex == nil { | ||
| crMutex = &CRMutex{} | ||
| } | ||
| return &HTTPAPI{ | ||
| client: client, | ||
| config: config, | ||
|
|
@@ -47,6 +76,7 @@ func NewAPIWithConfig(client client.Client, config Config, novaClient UsageNovaC | |
| usageMonitor: NewReportUsageAPIMonitor(), | ||
| capacityMonitor: NewReportCapacityAPIMonitor(), | ||
| infoMonitor: NewInfoAPIMonitor(), | ||
| crMutex: crMutex, | ||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 118
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 165
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 3527
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 1892
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 4468
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 6169
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 955
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 887
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 2820
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 4633
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 2152
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 5630
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 688
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 5176
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 47
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 47
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 47
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 2653
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 1283
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 2330
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 230
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 1948
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 47
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 5148
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 194
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 47
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 47
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 101
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 2273
🏁 Script executed:
# Verify the helm deployment splits - check if cortex-nova-knowledge and cortex-nova-scheduling are separate cat helm/bundles/cortex-nova/Chart.yamlRepository: cobaltcore-dev/cortex
Length of output: 1119
🏁 Script executed:
Repository: cobaltcore-dev/cortex
Length of output: 815
CRMutex provides no cross-pod synchronization in current deployment.
The syncer and API run in separate Kubernetes deployments:
cortex-nova-knowledge(syncer) andcortex-nova-scheduling(API). Each pod independently creates its owncrMutexinstance inmain.go. In multi-replica scenarios, a request to the API pod can race concurrent writes from the syncer running in a different pod, violating the atomicity guarantee claimed in the docstring. The current in-memory mutex only serializes within a single pod. A distributed coordination mechanism (e.g., Kubernetes Lease-based lock) is required to properly serialize CR state changes across pods.🤖 Prompt for AI Agents