Skip to content

Commit 010e42d

Browse files
authored
Pause cloud tasks for gated tenants (#3990)
1 parent 75ea486 commit 010e42d

File tree

4 files changed

+60
-15
lines changed

4 files changed

+60
-15
lines changed

backend/ee/onyx/configs/app_configs.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,5 @@
7777
HUBSPOT_TRACKING_URL = os.environ.get("HUBSPOT_TRACKING_URL")
7878

7979
ANONYMOUS_USER_COOKIE_NAME = "onyx_anonymous_user"
80+
81+
GATED_TENANTS_KEY = "gated_tenants"

backend/ee/onyx/server/tenants/api.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from ee.onyx.server.tenants.models import ProductGatingResponse
2828
from ee.onyx.server.tenants.models import SubscriptionSessionResponse
2929
from ee.onyx.server.tenants.models import SubscriptionStatusResponse
30+
from ee.onyx.server.tenants.product_gating import store_product_gating
3031
from ee.onyx.server.tenants.provisioning import delete_user_from_control_plane
3132
from ee.onyx.server.tenants.user_mapping import get_tenant_id_for_email
3233
from ee.onyx.server.tenants.user_mapping import remove_all_users_from_tenant
@@ -46,8 +47,6 @@
4647
from onyx.db.users import delete_user_from_db
4748
from onyx.db.users import get_user_by_email
4849
from onyx.server.manage.models import UserByEmail
49-
from onyx.server.settings.store import load_settings
50-
from onyx.server.settings.store import store_settings
5150
from onyx.utils.logger import setup_logger
5251
from shared_configs.contextvars import CURRENT_TENANT_ID_CONTEXTVAR
5352

@@ -133,21 +132,12 @@ def gate_product(
133132
"""
134133
Gating the product means that the product is not available to the tenant.
135134
They will be directed to the billing page.
136-
We gate the product when
137-
1) User has ended free trial without adding payment method
138-
2) User's card has declined
135+
We gate the product when their subscription has ended.
139136
"""
140137
try:
141-
tenant_id = product_gating_request.tenant_id
142-
token = CURRENT_TENANT_ID_CONTEXTVAR.set(tenant_id)
143-
144-
settings = load_settings()
145-
settings.application_status = product_gating_request.application_status
146-
store_settings(settings)
147-
148-
if token is not None:
149-
CURRENT_TENANT_ID_CONTEXTVAR.reset(token)
150-
138+
store_product_gating(
139+
product_gating_request.tenant_id, product_gating_request.application_status
140+
)
151141
return ProductGatingResponse(updated=True, error=None)
152142

153143
except Exception as e:
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from ee.onyx.configs.app_configs import GATED_TENANTS_KEY
2+
from onyx.configs.constants import ONYX_CLOUD_TENANT_ID
3+
from onyx.redis.redis_pool import get_redis_replica_client
4+
from onyx.server.settings.models import ApplicationStatus
5+
from onyx.server.settings.store import load_settings
6+
from onyx.server.settings.store import store_settings
7+
from onyx.setup import setup_logger
8+
from shared_configs.contextvars import CURRENT_TENANT_ID_CONTEXTVAR
9+
10+
logger = setup_logger()
11+
12+
13+
def update_tenant_gating(tenant_id: str, status: ApplicationStatus) -> None:
14+
redis_client = get_redis_replica_client(tenant_id=ONYX_CLOUD_TENANT_ID)
15+
16+
# Store the full status
17+
status_key = f"tenant:{tenant_id}:status"
18+
redis_client.set(status_key, status.value)
19+
20+
# Maintain the GATED_ACCESS set
21+
if status == ApplicationStatus.GATED_ACCESS:
22+
redis_client.sadd(GATED_TENANTS_KEY, tenant_id)
23+
else:
24+
redis_client.srem(GATED_TENANTS_KEY, tenant_id)
25+
26+
27+
def store_product_gating(tenant_id: str, application_status: ApplicationStatus) -> None:
28+
try:
29+
token = CURRENT_TENANT_ID_CONTEXTVAR.set(tenant_id)
30+
31+
settings = load_settings()
32+
settings.application_status = application_status
33+
store_settings(settings)
34+
35+
# Store gated tenant information in Redis
36+
update_tenant_gating(tenant_id, application_status)
37+
38+
if token is not None:
39+
CURRENT_TENANT_ID_CONTEXTVAR.reset(token)
40+
41+
except Exception:
42+
logger.exception("Failed to gate product")
43+
raise
44+
45+
46+
def get_gated_tenants() -> set[str]:
47+
redis_client = get_redis_replica_client(tenant_id=ONYX_CLOUD_TENANT_ID)
48+
return set(redis_client.smembers(GATED_TENANTS_KEY))

backend/onyx/background/celery/tasks/shared/tasks.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from redis.lock import Lock as RedisLock
99
from tenacity import RetryError
1010

11+
from ee.onyx.server.tenants.product_gating import get_gated_tenants
1112
from onyx.access.access import get_access_for_document
1213
from onyx.background.celery.apps.app_base import task_logger
1314
from onyx.background.celery.tasks.beat_schedule import BEAT_EXPIRES_DEFAULT
@@ -252,7 +253,11 @@ def cloud_beat_task_generator(
252253

253254
try:
254255
tenant_ids = get_all_tenant_ids()
256+
gated_tenants = get_gated_tenants()
255257
for tenant_id in tenant_ids:
258+
if tenant_id in gated_tenants:
259+
continue
260+
256261
current_time = time.monotonic()
257262
if current_time - last_lock_time >= (CELERY_GENERIC_BEAT_LOCK_TIMEOUT / 4):
258263
lock_beat.reacquire()

0 commit comments

Comments
 (0)