Skip to content

Commit

Permalink
Pause cloud tasks for gated tenants (#3990)
Browse files Browse the repository at this point in the history
  • Loading branch information
pablonyx authored Feb 13, 2025
1 parent 75ea486 commit 010e42d
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 15 deletions.
2 changes: 2 additions & 0 deletions backend/ee/onyx/configs/app_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,5 @@
HUBSPOT_TRACKING_URL = os.environ.get("HUBSPOT_TRACKING_URL")

ANONYMOUS_USER_COOKIE_NAME = "onyx_anonymous_user"

GATED_TENANTS_KEY = "gated_tenants"
20 changes: 5 additions & 15 deletions backend/ee/onyx/server/tenants/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from ee.onyx.server.tenants.models import ProductGatingResponse
from ee.onyx.server.tenants.models import SubscriptionSessionResponse
from ee.onyx.server.tenants.models import SubscriptionStatusResponse
from ee.onyx.server.tenants.product_gating import store_product_gating
from ee.onyx.server.tenants.provisioning import delete_user_from_control_plane
from ee.onyx.server.tenants.user_mapping import get_tenant_id_for_email
from ee.onyx.server.tenants.user_mapping import remove_all_users_from_tenant
Expand All @@ -46,8 +47,6 @@
from onyx.db.users import delete_user_from_db
from onyx.db.users import get_user_by_email
from onyx.server.manage.models import UserByEmail
from onyx.server.settings.store import load_settings
from onyx.server.settings.store import store_settings
from onyx.utils.logger import setup_logger
from shared_configs.contextvars import CURRENT_TENANT_ID_CONTEXTVAR

Expand Down Expand Up @@ -133,21 +132,12 @@ def gate_product(
"""
Gating the product means that the product is not available to the tenant.
They will be directed to the billing page.
We gate the product when
1) User has ended free trial without adding payment method
2) User's card has declined
We gate the product when their subscription has ended.
"""
try:
tenant_id = product_gating_request.tenant_id
token = CURRENT_TENANT_ID_CONTEXTVAR.set(tenant_id)

settings = load_settings()
settings.application_status = product_gating_request.application_status
store_settings(settings)

if token is not None:
CURRENT_TENANT_ID_CONTEXTVAR.reset(token)

store_product_gating(
product_gating_request.tenant_id, product_gating_request.application_status
)
return ProductGatingResponse(updated=True, error=None)

except Exception as e:
Expand Down
48 changes: 48 additions & 0 deletions backend/ee/onyx/server/tenants/product_gating.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from ee.onyx.configs.app_configs import GATED_TENANTS_KEY
from onyx.configs.constants import ONYX_CLOUD_TENANT_ID
from onyx.redis.redis_pool import get_redis_replica_client
from onyx.server.settings.models import ApplicationStatus
from onyx.server.settings.store import load_settings
from onyx.server.settings.store import store_settings
from onyx.setup import setup_logger
from shared_configs.contextvars import CURRENT_TENANT_ID_CONTEXTVAR

logger = setup_logger()


def update_tenant_gating(tenant_id: str, status: ApplicationStatus) -> None:
redis_client = get_redis_replica_client(tenant_id=ONYX_CLOUD_TENANT_ID)

# Store the full status
status_key = f"tenant:{tenant_id}:status"
redis_client.set(status_key, status.value)

# Maintain the GATED_ACCESS set
if status == ApplicationStatus.GATED_ACCESS:
redis_client.sadd(GATED_TENANTS_KEY, tenant_id)
else:
redis_client.srem(GATED_TENANTS_KEY, tenant_id)


def store_product_gating(tenant_id: str, application_status: ApplicationStatus) -> None:
try:
token = CURRENT_TENANT_ID_CONTEXTVAR.set(tenant_id)

settings = load_settings()
settings.application_status = application_status
store_settings(settings)

# Store gated tenant information in Redis
update_tenant_gating(tenant_id, application_status)

if token is not None:
CURRENT_TENANT_ID_CONTEXTVAR.reset(token)

except Exception:
logger.exception("Failed to gate product")
raise


def get_gated_tenants() -> set[str]:
redis_client = get_redis_replica_client(tenant_id=ONYX_CLOUD_TENANT_ID)
return set(redis_client.smembers(GATED_TENANTS_KEY))
5 changes: 5 additions & 0 deletions backend/onyx/background/celery/tasks/shared/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from redis.lock import Lock as RedisLock
from tenacity import RetryError

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

try:
tenant_ids = get_all_tenant_ids()
gated_tenants = get_gated_tenants()
for tenant_id in tenant_ids:
if tenant_id in gated_tenants:
continue

current_time = time.monotonic()
if current_time - last_lock_time >= (CELERY_GENERIC_BEAT_LOCK_TIMEOUT / 4):
lock_beat.reacquire()
Expand Down

0 comments on commit 010e42d

Please sign in to comment.