Skip to content

Commit 8cfbdff

Browse files
committed
Reconciling user and cluster quota limits.
1 parent 12665e8 commit 8cfbdff

File tree

2 files changed

+60
-13
lines changed

2 files changed

+60
-13
lines changed

ols/runners/quota_scheduler.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,24 @@
2929
"""
3030

3131

32-
RECONCILE_QUOTA_LIMITS_STATEMENT = """
32+
# Reconciliation SQL for user quotas - updates all user rows
33+
RECONCILE_USER_QUOTA_LIMITS_STATEMENT = """
3334
UPDATE quota_limits
3435
SET quota_limit = %s,
3536
available = available + (%s - quota_limit),
3637
updated_at = NOW()
37-
WHERE subject = %s
38+
WHERE subject = 'u'
39+
AND quota_limit != %s
40+
RETURNING id, quota_limit, available
41+
"""
42+
43+
# Reconciliation SQL for cluster quotas - updates cluster row
44+
RECONCILE_CLUSTER_QUOTA_LIMITS_STATEMENT = """
45+
UPDATE quota_limits
46+
SET quota_limit = %s,
47+
available = available + (%s - quota_limit),
48+
updated_at = NOW()
49+
WHERE subject = 'c'
3850
AND quota_limit != %s
3951
RETURNING id, quota_limit, available
4052
"""
@@ -174,17 +186,25 @@ def reconcile_quota_limits(
174186
logger.debug("No initial quota set for '%s', skipping reconciliation", name)
175187
return
176188

177-
subject_id = get_subject_id(quota_limiter.type)
189+
# Select appropriate SQL based on limiter type
190+
if quota_limiter.type == constants.USER_QUOTA_LIMITER:
191+
reconcile_sql = RECONCILE_USER_QUOTA_LIMITS_STATEMENT
192+
elif quota_limiter.type == constants.CLUSTER_QUOTA_LIMITER:
193+
reconcile_sql = RECONCILE_CLUSTER_QUOTA_LIMITS_STATEMENT
194+
else:
195+
logger.error("Unknown limiter type '%s' for '%s', skipping reconciliation",
196+
quota_limiter.type, name)
197+
return
198+
178199
new_quota = quota_limiter.initial_quota
179200

180201
try:
181202
with connection.cursor() as cursor:
182203
cursor.execute(
183-
RECONCILE_QUOTA_LIMITS_STATEMENT,
204+
reconcile_sql,
184205
(
185206
new_quota, # new quota_limit value
186207
new_quota, # used in calculation: available + (new - old)
187-
subject_id, # WHERE subject = ?
188208
new_quota, # WHERE quota_limit != ?
189209
),
190210
)

ols/src/quota/revokable_quota_limiter.py

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,24 @@ class RevokableQuotaLimiter(QuotaLimiter):
4949
WHERE id=%s and subject=%s
5050
"""
5151

52-
RECONCILE_QUOTA_LIMITS = """
52+
# Reconciliation SQL for user quotas - updates all user rows
53+
RECONCILE_USER_QUOTA_LIMITS = """
5354
UPDATE quota_limits
5455
SET quota_limit = %s,
5556
available = available + (%s - quota_limit),
5657
updated_at = NOW()
57-
WHERE subject = %s
58+
WHERE subject = 'u'
59+
AND quota_limit != %s
60+
RETURNING id, quota_limit, available
61+
"""
62+
63+
# Reconciliation SQL for cluster quotas - updates cluster row
64+
RECONCILE_CLUSTER_QUOTA_LIMITS = """
65+
UPDATE quota_limits
66+
SET quota_limit = %s,
67+
available = available + (%s - quota_limit),
68+
updated_at = NOW()
69+
WHERE subject = 'c'
5870
AND quota_limit != %s
5971
RETURNING id, quota_limit, available
6072
"""
@@ -75,11 +87,18 @@ def __init__(
7587
def connect(self) -> None:
7688
"""Initialize connection to database and reconcile quota limits.
7789
78-
After establishing the database connection, this method automatically
79-
reconciles any existing quota records with the current configuration.
90+
Reconciliation ensures database quota limits match OLSConfig after updates.
91+
This happens on first request after config changes, ensuring immediate sync.
92+
The quota_scheduler also runs reconciliation periodically as a backup.
8093
"""
8194
super().connect()
82-
self._reconcile_quota_limits()
95+
# Reconcile on connect so changes take effect on first request after OLSConfig update
96+
try:
97+
self._reconcile_quota_limits()
98+
except Exception as e:
99+
logger.warning(
100+
"Quota reconciliation on connect failed (will retry on scheduler): %s", e
101+
)
83102

84103
@connection
85104
def available_quota(self, subject_id: str = "") -> int:
@@ -199,15 +218,23 @@ def _reconcile_quota_limits(self) -> None:
199218
Updates existing quota records when initial_quota changes while preserving
200219
consumed tokens.
201220
"""
221+
# Select appropriate SQL based on subject type
222+
if self.subject_type == "u":
223+
reconcile_sql = RevokableQuotaLimiter.RECONCILE_USER_QUOTA_LIMITS
224+
elif self.subject_type == "c":
225+
reconcile_sql = RevokableQuotaLimiter.RECONCILE_CLUSTER_QUOTA_LIMITS
226+
else:
227+
logger.error("Unknown subject type '%s', skipping reconciliation", self.subject_type)
228+
return
229+
202230
try:
203231
with self.connection.cursor() as cursor:
204232
cursor.execute(
205-
RevokableQuotaLimiter.RECONCILE_QUOTA_LIMITS,
233+
reconcile_sql,
206234
(
207235
self.initial_quota, # new quota_limit
208236
self.initial_quota, # used in calculation: available + (new - old)
209-
self.subject_type,
210-
self.initial_quota,
237+
self.initial_quota, # WHERE quota_limit != ?
211238
),
212239
)
213240
updated_rows = cursor.fetchall()

0 commit comments

Comments
 (0)