Free validator ports in the cloud on clear/cancel/delete
An address still being associated (assigning_fip) has no fip_id in the database, so "clear queue" did not detach it, and the association then finished after the row was gone, leaving the floating IP on the validator port for good. - After clear/cancel/delete, ask the cloud which floating IPs sit on the affected validator ports (new ListFloatingIPsByPort) and detach those that this system queued (known in ip_registry); foreign ones are left. - SetFIPAssociated applies only to a row still in assigning_fip; if the address was removed meanwhile, associateFIP detaches the floating IP. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c0e300f71c
commit
1ee5757004
8 files changed
+287
-5
No files matched your search
@@ -122,13 +122,59 @@ func (d *DB) ClaimNextQueued(ctx context.Context, validatorID string, leaseTTL t
|
||||
return &item, nil
|
||||
}
|
||||
|
||||
// SetFIPAssociated records that the floating IP is now attached. It only
|
||||
// applies to a row still in assigning_fip: if the address was deleted or
|
||||
// cancelled while the cloud call was in flight, it returns ErrInvalidState
|
||||
// and the caller must detach the floating IP again.
|
||||
func (d *DB) SetFIPAssociated(ctx context.Context, ipID int64, fipID string, leaseTTL time.Duration) error {
|
||||
now := Now()
|
||||
_, err := d.ExecContext(ctx, `
|
||||
res, err := d.ExecContext(ctx, `
|
||||
UPDATE ip_queue SET state=?, fip_id=?, fip_associated_at=?, lease_expires_at=?, updated_at=?
|
||||
WHERE id=?
|
||||
`, IPAwaitingSelfCheck, fipID, timeToDB(now), timeToDB(now.Add(leaseTTL)), timeToDB(now), ipID)
|
||||
return err
|
||||
WHERE id=? AND state=?
|
||||
`, IPAwaitingSelfCheck, fipID, timeToDB(now), timeToDB(now.Add(leaseTTL)), timeToDB(now), ipID, IPAssigningFIP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n, _ := res.RowsAffected(); n == 0 {
|
||||
return fmt.Errorf("ip_id %d is no longer assigning_fip: %w", ipID, ErrInvalidState)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// KnownAddresses returns the subset of addresses present in ip_registry,
|
||||
// i.e. addresses this system has ever queued.
|
||||
func (d *DB) KnownAddresses(ctx context.Context, addresses []string) (map[string]bool, error) {
|
||||
const chunk = 500
|
||||
known := make(map[string]bool, len(addresses))
|
||||
for start := 0; start < len(addresses); start += chunk {
|
||||
end := start + chunk
|
||||
if end > len(addresses) {
|
||||
end = len(addresses)
|
||||
}
|
||||
part := addresses[start:end]
|
||||
args := make([]any, len(part))
|
||||
for i, a := range part {
|
||||
args[i] = a
|
||||
}
|
||||
rows, err := d.QueryContext(ctx, `SELECT ip_address FROM ip_registry WHERE ip_address IN (`+placeholders(len(part))+`)`, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for rows.Next() {
|
||||
var a string
|
||||
if err := rows.Scan(&a); err != nil {
|
||||
rows.Close()
|
||||
return nil, err
|
||||
}
|
||||
known[a] = true
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
rows.Close()
|
||||
return nil, err
|
||||
}
|
||||
rows.Close()
|
||||
}
|
||||
return known, nil
|
||||
}
|
||||
|
||||
func (d *DB) SetChecking(ctx context.Context, ipID int64, leaseTTL time.Duration) error {
|
||||
|
||||
Reference in new issue
Block a user