mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-06-25 23:40:58 +02:00
Merge pull request #3856 from onyx-dot-app/feature/no_scan_iter
lessen usage of scan_iter
This commit is contained in:
commit
c0271a948a
@ -21,13 +21,16 @@ from onyx.background.celery.tasks.indexing.utils import (
|
|||||||
get_unfenced_index_attempt_ids,
|
get_unfenced_index_attempt_ids,
|
||||||
)
|
)
|
||||||
from onyx.configs.constants import CELERY_PRIMARY_WORKER_LOCK_TIMEOUT
|
from onyx.configs.constants import CELERY_PRIMARY_WORKER_LOCK_TIMEOUT
|
||||||
|
from onyx.configs.constants import OnyxRedisConstants
|
||||||
from onyx.configs.constants import OnyxRedisLocks
|
from onyx.configs.constants import OnyxRedisLocks
|
||||||
from onyx.configs.constants import POSTGRES_CELERY_WORKER_PRIMARY_APP_NAME
|
from onyx.configs.constants import POSTGRES_CELERY_WORKER_PRIMARY_APP_NAME
|
||||||
from onyx.db.engine import get_session_with_default_tenant
|
from onyx.db.engine import get_session_with_default_tenant
|
||||||
from onyx.db.engine import SqlEngine
|
from onyx.db.engine import SqlEngine
|
||||||
from onyx.db.index_attempt import get_index_attempt
|
from onyx.db.index_attempt import get_index_attempt
|
||||||
from onyx.db.index_attempt import mark_attempt_canceled
|
from onyx.db.index_attempt import mark_attempt_canceled
|
||||||
from onyx.redis.redis_connector_credential_pair import RedisConnectorCredentialPair
|
from onyx.redis.redis_connector_credential_pair import (
|
||||||
|
RedisGlobalConnectorCredentialPair,
|
||||||
|
)
|
||||||
from onyx.redis.redis_connector_delete import RedisConnectorDelete
|
from onyx.redis.redis_connector_delete import RedisConnectorDelete
|
||||||
from onyx.redis.redis_connector_doc_perm_sync import RedisConnectorPermissionSync
|
from onyx.redis.redis_connector_doc_perm_sync import RedisConnectorPermissionSync
|
||||||
from onyx.redis.redis_connector_ext_group_sync import RedisConnectorExternalGroupSync
|
from onyx.redis.redis_connector_ext_group_sync import RedisConnectorExternalGroupSync
|
||||||
@ -141,23 +144,16 @@ def on_worker_init(sender: Worker, **kwargs: Any) -> None:
|
|||||||
r.delete(OnyxRedisLocks.CHECK_VESPA_SYNC_BEAT_LOCK)
|
r.delete(OnyxRedisLocks.CHECK_VESPA_SYNC_BEAT_LOCK)
|
||||||
r.delete(OnyxRedisLocks.MONITOR_VESPA_SYNC_BEAT_LOCK)
|
r.delete(OnyxRedisLocks.MONITOR_VESPA_SYNC_BEAT_LOCK)
|
||||||
|
|
||||||
r.delete(RedisConnectorCredentialPair.get_taskset_key())
|
r.delete(OnyxRedisConstants.ACTIVE_FENCES)
|
||||||
r.delete(RedisConnectorCredentialPair.get_fence_key())
|
|
||||||
|
|
||||||
|
RedisGlobalConnectorCredentialPair.reset_all(r)
|
||||||
RedisDocumentSet.reset_all(r)
|
RedisDocumentSet.reset_all(r)
|
||||||
|
|
||||||
RedisUserGroup.reset_all(r)
|
RedisUserGroup.reset_all(r)
|
||||||
|
|
||||||
RedisConnectorDelete.reset_all(r)
|
RedisConnectorDelete.reset_all(r)
|
||||||
|
|
||||||
RedisConnectorPrune.reset_all(r)
|
RedisConnectorPrune.reset_all(r)
|
||||||
|
|
||||||
RedisConnectorIndex.reset_all(r)
|
RedisConnectorIndex.reset_all(r)
|
||||||
|
|
||||||
RedisConnectorStop.reset_all(r)
|
RedisConnectorStop.reset_all(r)
|
||||||
|
|
||||||
RedisConnectorPermissionSync.reset_all(r)
|
RedisConnectorPermissionSync.reset_all(r)
|
||||||
|
|
||||||
RedisConnectorExternalGroupSync.reset_all(r)
|
RedisConnectorExternalGroupSync.reset_all(r)
|
||||||
|
|
||||||
# mark orphaned index attempts as failed
|
# mark orphaned index attempts as failed
|
||||||
|
@ -18,7 +18,7 @@ BEAT_EXPIRES_DEFAULT = 15 * 60 # 15 minutes (in seconds)
|
|||||||
|
|
||||||
# hack to slow down task dispatch in the cloud until
|
# hack to slow down task dispatch in the cloud until
|
||||||
# we have a better implementation (backpressure, etc)
|
# we have a better implementation (backpressure, etc)
|
||||||
CLOUD_BEAT_SCHEDULE_MULTIPLIER = 8
|
CLOUD_BEAT_SCHEDULE_MULTIPLIER = 4
|
||||||
|
|
||||||
# tasks that only run in the cloud
|
# tasks that only run in the cloud
|
||||||
# the name attribute must start with ONYX_CLOUD_CELERY_TASK_PREFIX = "cloud" to be filtered
|
# the name attribute must start with ONYX_CLOUD_CELERY_TASK_PREFIX = "cloud" to be filtered
|
||||||
|
@ -3,6 +3,7 @@ from datetime import datetime
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from datetime import timezone
|
from datetime import timezone
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
from typing import Any
|
||||||
from typing import cast
|
from typing import cast
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
@ -38,6 +39,7 @@ from onyx.configs.constants import DocumentSource
|
|||||||
from onyx.configs.constants import OnyxCeleryPriority
|
from onyx.configs.constants import OnyxCeleryPriority
|
||||||
from onyx.configs.constants import OnyxCeleryQueues
|
from onyx.configs.constants import OnyxCeleryQueues
|
||||||
from onyx.configs.constants import OnyxCeleryTask
|
from onyx.configs.constants import OnyxCeleryTask
|
||||||
|
from onyx.configs.constants import OnyxRedisConstants
|
||||||
from onyx.configs.constants import OnyxRedisLocks
|
from onyx.configs.constants import OnyxRedisLocks
|
||||||
from onyx.configs.constants import OnyxRedisSignals
|
from onyx.configs.constants import OnyxRedisSignals
|
||||||
from onyx.db.connector import mark_cc_pair_as_permissions_synced
|
from onyx.db.connector import mark_cc_pair_as_permissions_synced
|
||||||
@ -57,8 +59,8 @@ from onyx.redis.redis_connector import RedisConnector
|
|||||||
from onyx.redis.redis_connector_doc_perm_sync import RedisConnectorPermissionSync
|
from onyx.redis.redis_connector_doc_perm_sync import RedisConnectorPermissionSync
|
||||||
from onyx.redis.redis_connector_doc_perm_sync import RedisConnectorPermissionSyncPayload
|
from onyx.redis.redis_connector_doc_perm_sync import RedisConnectorPermissionSyncPayload
|
||||||
from onyx.redis.redis_pool import get_redis_client
|
from onyx.redis.redis_pool import get_redis_client
|
||||||
|
from onyx.redis.redis_pool import get_redis_replica_client
|
||||||
from onyx.redis.redis_pool import redis_lock_dump
|
from onyx.redis.redis_pool import redis_lock_dump
|
||||||
from onyx.redis.redis_pool import SCAN_ITER_COUNT_DEFAULT
|
|
||||||
from onyx.server.utils import make_short_id
|
from onyx.server.utils import make_short_id
|
||||||
from onyx.utils.logger import doc_permission_sync_ctx
|
from onyx.utils.logger import doc_permission_sync_ctx
|
||||||
from onyx.utils.logger import LoggerContextVars
|
from onyx.utils.logger import LoggerContextVars
|
||||||
@ -123,6 +125,7 @@ def check_for_doc_permissions_sync(self: Task, *, tenant_id: str | None) -> bool
|
|||||||
# we need to use celery's redis client to access its redis data
|
# we need to use celery's redis client to access its redis data
|
||||||
# (which lives on a different db number)
|
# (which lives on a different db number)
|
||||||
r = get_redis_client(tenant_id=tenant_id)
|
r = get_redis_client(tenant_id=tenant_id)
|
||||||
|
r_replica = get_redis_replica_client(tenant_id=tenant_id)
|
||||||
r_celery: Redis = self.app.broker_connection().channel().client # type: ignore
|
r_celery: Redis = self.app.broker_connection().channel().client # type: ignore
|
||||||
|
|
||||||
lock_beat: RedisLock = r.lock(
|
lock_beat: RedisLock = r.lock(
|
||||||
@ -158,18 +161,20 @@ def check_for_doc_permissions_sync(self: Task, *, tenant_id: str | None) -> bool
|
|||||||
|
|
||||||
# we want to run this less frequently than the overall task
|
# we want to run this less frequently than the overall task
|
||||||
lock_beat.reacquire()
|
lock_beat.reacquire()
|
||||||
if not r.exists(OnyxRedisSignals.VALIDATE_PERMISSION_SYNC_FENCES):
|
if not r.exists(OnyxRedisSignals.BLOCK_VALIDATE_PERMISSION_SYNC_FENCES):
|
||||||
# clear any permission fences that don't have associated celery tasks in progress
|
# clear any permission fences that don't have associated celery tasks in progress
|
||||||
# tasks can be in the queue in redis, in reserved tasks (prefetched by the worker),
|
# tasks can be in the queue in redis, in reserved tasks (prefetched by the worker),
|
||||||
# or be currently executing
|
# or be currently executing
|
||||||
try:
|
try:
|
||||||
validate_permission_sync_fences(tenant_id, r, r_celery, lock_beat)
|
validate_permission_sync_fences(
|
||||||
|
tenant_id, r, r_replica, r_celery, lock_beat
|
||||||
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
task_logger.exception(
|
task_logger.exception(
|
||||||
"Exception while validating permission sync fences"
|
"Exception while validating permission sync fences"
|
||||||
)
|
)
|
||||||
|
|
||||||
r.set(OnyxRedisSignals.VALIDATE_PERMISSION_SYNC_FENCES, 1, ex=60)
|
r.set(OnyxRedisSignals.BLOCK_VALIDATE_PERMISSION_SYNC_FENCES, 1, ex=300)
|
||||||
except SoftTimeLimitExceeded:
|
except SoftTimeLimitExceeded:
|
||||||
task_logger.info(
|
task_logger.info(
|
||||||
"Soft time limit exceeded, task is being terminated gracefully."
|
"Soft time limit exceeded, task is being terminated gracefully."
|
||||||
@ -486,6 +491,7 @@ def update_external_document_permissions_task(
|
|||||||
def validate_permission_sync_fences(
|
def validate_permission_sync_fences(
|
||||||
tenant_id: str | None,
|
tenant_id: str | None,
|
||||||
r: Redis,
|
r: Redis,
|
||||||
|
r_replica: Redis,
|
||||||
r_celery: Redis,
|
r_celery: Redis,
|
||||||
lock_beat: RedisLock,
|
lock_beat: RedisLock,
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -506,12 +512,15 @@ def validate_permission_sync_fences(
|
|||||||
OnyxCeleryQueues.CONNECTOR_DOC_PERMISSIONS_SYNC, r_celery
|
OnyxCeleryQueues.CONNECTOR_DOC_PERMISSIONS_SYNC, r_celery
|
||||||
)
|
)
|
||||||
|
|
||||||
# validate all existing indexing jobs
|
# validate all existing permission sync jobs
|
||||||
for key_bytes in r.scan_iter(
|
lock_beat.reacquire()
|
||||||
RedisConnectorPermissionSync.FENCE_PREFIX + "*",
|
keys = cast(set[Any], r_replica.smembers(OnyxRedisConstants.ACTIVE_FENCES))
|
||||||
count=SCAN_ITER_COUNT_DEFAULT,
|
for key in keys:
|
||||||
):
|
key_bytes = cast(bytes, key)
|
||||||
lock_beat.reacquire()
|
key_str = key_bytes.decode("utf-8")
|
||||||
|
if not key_str.startswith(RedisConnectorPermissionSync.FENCE_PREFIX):
|
||||||
|
continue
|
||||||
|
|
||||||
validate_permission_sync_fence(
|
validate_permission_sync_fence(
|
||||||
tenant_id,
|
tenant_id,
|
||||||
key_bytes,
|
key_bytes,
|
||||||
@ -520,6 +529,9 @@ def validate_permission_sync_fences(
|
|||||||
r,
|
r,
|
||||||
r_celery,
|
r_celery,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
lock_beat.reacquire()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@ -224,7 +224,7 @@ def check_for_indexing(self: Task, *, tenant_id: str | None) -> int | None:
|
|||||||
|
|
||||||
lock_beat.reacquire()
|
lock_beat.reacquire()
|
||||||
# we want to run this less frequently than the overall task
|
# we want to run this less frequently than the overall task
|
||||||
if not redis_client.exists(OnyxRedisSignals.VALIDATE_INDEXING_FENCES):
|
if not redis_client.exists(OnyxRedisSignals.BLOCK_VALIDATE_INDEXING_FENCES):
|
||||||
# clear any indexing fences that don't have associated celery tasks in progress
|
# clear any indexing fences that don't have associated celery tasks in progress
|
||||||
# tasks can be in the queue in redis, in reserved tasks (prefetched by the worker),
|
# tasks can be in the queue in redis, in reserved tasks (prefetched by the worker),
|
||||||
# or be currently executing
|
# or be currently executing
|
||||||
@ -235,7 +235,7 @@ def check_for_indexing(self: Task, *, tenant_id: str | None) -> int | None:
|
|||||||
except Exception:
|
except Exception:
|
||||||
task_logger.exception("Exception while validating indexing fences")
|
task_logger.exception("Exception while validating indexing fences")
|
||||||
|
|
||||||
redis_client.set(OnyxRedisSignals.VALIDATE_INDEXING_FENCES, 1, ex=60)
|
redis_client.set(OnyxRedisSignals.BLOCK_VALIDATE_INDEXING_FENCES, 1, ex=60)
|
||||||
except SoftTimeLimitExceeded:
|
except SoftTimeLimitExceeded:
|
||||||
task_logger.info(
|
task_logger.info(
|
||||||
"Soft time limit exceeded, task is being terminated gracefully."
|
"Soft time limit exceeded, task is being terminated gracefully."
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from datetime import timezone
|
from datetime import timezone
|
||||||
|
from typing import Any
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
import redis
|
import redis
|
||||||
from celery import Celery
|
from celery import Celery
|
||||||
@ -19,6 +21,7 @@ from onyx.configs.constants import DocumentSource
|
|||||||
from onyx.configs.constants import OnyxCeleryPriority
|
from onyx.configs.constants import OnyxCeleryPriority
|
||||||
from onyx.configs.constants import OnyxCeleryQueues
|
from onyx.configs.constants import OnyxCeleryQueues
|
||||||
from onyx.configs.constants import OnyxCeleryTask
|
from onyx.configs.constants import OnyxCeleryTask
|
||||||
|
from onyx.configs.constants import OnyxRedisConstants
|
||||||
from onyx.db.engine import get_db_current_time
|
from onyx.db.engine import get_db_current_time
|
||||||
from onyx.db.engine import get_session_with_tenant
|
from onyx.db.engine import get_session_with_tenant
|
||||||
from onyx.db.enums import ConnectorCredentialPairStatus
|
from onyx.db.enums import ConnectorCredentialPairStatus
|
||||||
@ -37,7 +40,6 @@ from onyx.redis.redis_connector import RedisConnector
|
|||||||
from onyx.redis.redis_connector_index import RedisConnectorIndex
|
from onyx.redis.redis_connector_index import RedisConnectorIndex
|
||||||
from onyx.redis.redis_connector_index import RedisConnectorIndexPayload
|
from onyx.redis.redis_connector_index import RedisConnectorIndexPayload
|
||||||
from onyx.redis.redis_pool import redis_lock_dump
|
from onyx.redis.redis_pool import redis_lock_dump
|
||||||
from onyx.redis.redis_pool import SCAN_ITER_COUNT_DEFAULT
|
|
||||||
from onyx.utils.logger import setup_logger
|
from onyx.utils.logger import setup_logger
|
||||||
|
|
||||||
logger = setup_logger()
|
logger = setup_logger()
|
||||||
@ -304,10 +306,13 @@ def validate_indexing_fences(
|
|||||||
|
|
||||||
# Use replica for this because the worst thing that happens
|
# Use replica for this because the worst thing that happens
|
||||||
# is that we don't run the validation on this pass
|
# is that we don't run the validation on this pass
|
||||||
for key_bytes in r_replica.scan_iter(
|
keys = cast(set[Any], r_replica.smembers(OnyxRedisConstants.ACTIVE_FENCES))
|
||||||
RedisConnectorIndex.FENCE_PREFIX + "*", count=SCAN_ITER_COUNT_DEFAULT
|
for key in keys:
|
||||||
):
|
key_bytes = cast(bytes, key)
|
||||||
lock_beat.reacquire()
|
key_str = key_bytes.decode("utf-8")
|
||||||
|
if not key_str.startswith(RedisConnectorIndex.FENCE_PREFIX):
|
||||||
|
continue
|
||||||
|
|
||||||
with get_session_with_tenant(tenant_id) as db_session:
|
with get_session_with_tenant(tenant_id) as db_session:
|
||||||
validate_indexing_fence(
|
validate_indexing_fence(
|
||||||
tenant_id,
|
tenant_id,
|
||||||
@ -316,6 +321,9 @@ def validate_indexing_fences(
|
|||||||
r_celery,
|
r_celery,
|
||||||
db_session,
|
db_session,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
lock_beat.reacquire()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,7 +36,9 @@ from onyx.configs.app_configs import VESPA_SYNC_MAX_TASKS
|
|||||||
from onyx.configs.constants import CELERY_VESPA_SYNC_BEAT_LOCK_TIMEOUT
|
from onyx.configs.constants import CELERY_VESPA_SYNC_BEAT_LOCK_TIMEOUT
|
||||||
from onyx.configs.constants import OnyxCeleryQueues
|
from onyx.configs.constants import OnyxCeleryQueues
|
||||||
from onyx.configs.constants import OnyxCeleryTask
|
from onyx.configs.constants import OnyxCeleryTask
|
||||||
|
from onyx.configs.constants import OnyxRedisConstants
|
||||||
from onyx.configs.constants import OnyxRedisLocks
|
from onyx.configs.constants import OnyxRedisLocks
|
||||||
|
from onyx.configs.constants import OnyxRedisSignals
|
||||||
from onyx.db.connector import fetch_connector_by_id
|
from onyx.db.connector import fetch_connector_by_id
|
||||||
from onyx.db.connector_credential_pair import add_deletion_failure_message
|
from onyx.db.connector_credential_pair import add_deletion_failure_message
|
||||||
from onyx.db.connector_credential_pair import (
|
from onyx.db.connector_credential_pair import (
|
||||||
@ -72,6 +74,9 @@ from onyx.document_index.interfaces import VespaDocumentFields
|
|||||||
from onyx.httpx.httpx_pool import HttpxPool
|
from onyx.httpx.httpx_pool import HttpxPool
|
||||||
from onyx.redis.redis_connector import RedisConnector
|
from onyx.redis.redis_connector import RedisConnector
|
||||||
from onyx.redis.redis_connector_credential_pair import RedisConnectorCredentialPair
|
from onyx.redis.redis_connector_credential_pair import RedisConnectorCredentialPair
|
||||||
|
from onyx.redis.redis_connector_credential_pair import (
|
||||||
|
RedisGlobalConnectorCredentialPair,
|
||||||
|
)
|
||||||
from onyx.redis.redis_connector_delete import RedisConnectorDelete
|
from onyx.redis.redis_connector_delete import RedisConnectorDelete
|
||||||
from onyx.redis.redis_connector_doc_perm_sync import RedisConnectorPermissionSync
|
from onyx.redis.redis_connector_doc_perm_sync import RedisConnectorPermissionSync
|
||||||
from onyx.redis.redis_connector_index import RedisConnectorIndex
|
from onyx.redis.redis_connector_index import RedisConnectorIndex
|
||||||
@ -204,10 +209,12 @@ def try_generate_stale_document_sync_tasks(
|
|||||||
tenant_id: str | None,
|
tenant_id: str | None,
|
||||||
) -> int | None:
|
) -> int | None:
|
||||||
# the fence is up, do nothing
|
# the fence is up, do nothing
|
||||||
if r.exists(RedisConnectorCredentialPair.get_fence_key()):
|
|
||||||
|
redis_global_ccpair = RedisGlobalConnectorCredentialPair(r)
|
||||||
|
if redis_global_ccpair.fenced:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
r.delete(RedisConnectorCredentialPair.get_taskset_key()) # delete the taskset
|
redis_global_ccpair.delete_taskset()
|
||||||
|
|
||||||
# add tasks to celery and build up the task set to monitor in redis
|
# add tasks to celery and build up the task set to monitor in redis
|
||||||
stale_doc_count = count_documents_by_needs_sync(db_session)
|
stale_doc_count = count_documents_by_needs_sync(db_session)
|
||||||
@ -265,7 +272,7 @@ def try_generate_stale_document_sync_tasks(
|
|||||||
f"RedisConnector.generate_tasks finished for all cc_pairs. total_tasks_generated={total_tasks_generated}"
|
f"RedisConnector.generate_tasks finished for all cc_pairs. total_tasks_generated={total_tasks_generated}"
|
||||||
)
|
)
|
||||||
|
|
||||||
r.set(RedisConnectorCredentialPair.get_fence_key(), total_tasks_generated)
|
redis_global_ccpair.set_fence(total_tasks_generated)
|
||||||
return total_tasks_generated
|
return total_tasks_generated
|
||||||
|
|
||||||
|
|
||||||
@ -416,23 +423,17 @@ def try_generate_user_group_sync_tasks(
|
|||||||
|
|
||||||
|
|
||||||
def monitor_connector_taskset(r: Redis) -> None:
|
def monitor_connector_taskset(r: Redis) -> None:
|
||||||
fence_value = r.get(RedisConnectorCredentialPair.get_fence_key())
|
redis_global_ccpair = RedisGlobalConnectorCredentialPair(r)
|
||||||
if fence_value is None:
|
initial_count = redis_global_ccpair.payload
|
||||||
|
if initial_count is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
remaining = redis_global_ccpair.get_remaining()
|
||||||
initial_count = int(cast(int, fence_value))
|
|
||||||
except ValueError:
|
|
||||||
task_logger.error("The value is not an integer.")
|
|
||||||
return
|
|
||||||
|
|
||||||
count = r.scard(RedisConnectorCredentialPair.get_taskset_key())
|
|
||||||
task_logger.info(
|
task_logger.info(
|
||||||
f"Stale document sync progress: remaining={count} initial={initial_count}"
|
f"Stale document sync progress: remaining={remaining} initial={initial_count}"
|
||||||
)
|
)
|
||||||
if count == 0:
|
if remaining == 0:
|
||||||
r.delete(RedisConnectorCredentialPair.get_taskset_key())
|
redis_global_ccpair.reset()
|
||||||
r.delete(RedisConnectorCredentialPair.get_fence_key())
|
|
||||||
task_logger.info(f"Successfully synced stale documents. count={initial_count}")
|
task_logger.info(f"Successfully synced stale documents. count={initial_count}")
|
||||||
|
|
||||||
|
|
||||||
@ -820,9 +821,6 @@ def monitor_vespa_sync(self: Task, tenant_id: str | None) -> bool | None:
|
|||||||
|
|
||||||
time_start = time.monotonic()
|
time_start = time.monotonic()
|
||||||
|
|
||||||
timings: dict[str, Any] = {}
|
|
||||||
timings["start"] = time_start
|
|
||||||
|
|
||||||
r = get_redis_client(tenant_id=tenant_id)
|
r = get_redis_client(tenant_id=tenant_id)
|
||||||
|
|
||||||
# Replica usage notes
|
# Replica usage notes
|
||||||
@ -847,7 +845,7 @@ def monitor_vespa_sync(self: Task, tenant_id: str | None) -> bool | None:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# print current queue lengths
|
# print current queue lengths
|
||||||
phase_start = time.monotonic()
|
time.monotonic()
|
||||||
# we don't need every tenant polling redis for this info.
|
# we don't need every tenant polling redis for this info.
|
||||||
if not MULTI_TENANT or random.randint(1, 10) == 10:
|
if not MULTI_TENANT or random.randint(1, 10) == 10:
|
||||||
r_celery = self.app.broker_connection().channel().client # type: ignore
|
r_celery = self.app.broker_connection().channel().client # type: ignore
|
||||||
@ -889,50 +887,38 @@ def monitor_vespa_sync(self: Task, tenant_id: str | None) -> bool | None:
|
|||||||
f"external_group_sync={n_external_group_sync} "
|
f"external_group_sync={n_external_group_sync} "
|
||||||
f"permissions_upsert={n_permissions_upsert} "
|
f"permissions_upsert={n_permissions_upsert} "
|
||||||
)
|
)
|
||||||
timings["queues"] = time.monotonic() - phase_start
|
|
||||||
timings["queues_ttl"] = r.ttl(OnyxRedisLocks.MONITOR_VESPA_SYNC_BEAT_LOCK)
|
|
||||||
|
|
||||||
# scan and monitor activity to completion
|
# we want to run this less frequently than the overall task
|
||||||
phase_start = time.monotonic()
|
if not r.exists(OnyxRedisSignals.BLOCK_BUILD_FENCE_LOOKUP_TABLE):
|
||||||
lock_beat.reacquire()
|
# build a lookup table of existing fences
|
||||||
if r_replica.exists(RedisConnectorCredentialPair.get_fence_key()):
|
# this is just a migration concern and should be unnecessary once
|
||||||
if r.exists(RedisConnectorCredentialPair.get_fence_key()):
|
# lookup tables are rolled out
|
||||||
|
for key_bytes in r_replica.scan_iter(count=SCAN_ITER_COUNT_DEFAULT):
|
||||||
|
if is_fence(key_bytes) and not r.sismember(
|
||||||
|
OnyxRedisConstants.ACTIVE_FENCES, key_bytes
|
||||||
|
):
|
||||||
|
logger.warning(f"Adding {key_bytes} to the lookup table.")
|
||||||
|
r.sadd(OnyxRedisConstants.ACTIVE_FENCES, key_bytes)
|
||||||
|
|
||||||
|
r.set(OnyxRedisSignals.BLOCK_BUILD_FENCE_LOOKUP_TABLE, 1, ex=300)
|
||||||
|
|
||||||
|
# use a lookup table to find active fences. We still have to verify the fence
|
||||||
|
# exists since it is an optimization and not the source of truth.
|
||||||
|
keys = cast(set[Any], r.smembers(OnyxRedisConstants.ACTIVE_FENCES))
|
||||||
|
for key in keys:
|
||||||
|
key_bytes = cast(bytes, key)
|
||||||
|
|
||||||
|
if not r.exists(key_bytes):
|
||||||
|
r.srem(OnyxRedisConstants.ACTIVE_FENCES, key_bytes)
|
||||||
|
continue
|
||||||
|
|
||||||
|
key_str = key_bytes.decode("utf-8")
|
||||||
|
if key_str == RedisGlobalConnectorCredentialPair.FENCE_KEY:
|
||||||
monitor_connector_taskset(r)
|
monitor_connector_taskset(r)
|
||||||
timings["connector"] = time.monotonic() - phase_start
|
elif key_str.startswith(RedisDocumentSet.FENCE_PREFIX):
|
||||||
timings["connector_ttl"] = r.ttl(OnyxRedisLocks.MONITOR_VESPA_SYNC_BEAT_LOCK)
|
|
||||||
|
|
||||||
phase_start = time.monotonic()
|
|
||||||
lock_beat.reacquire()
|
|
||||||
for key_bytes in r_replica.scan_iter(
|
|
||||||
RedisConnectorDelete.FENCE_PREFIX + "*", count=SCAN_ITER_COUNT_DEFAULT
|
|
||||||
):
|
|
||||||
if r.exists(key_bytes):
|
|
||||||
monitor_connector_deletion_taskset(tenant_id, key_bytes, r)
|
|
||||||
lock_beat.reacquire()
|
|
||||||
|
|
||||||
timings["connector_deletion"] = time.monotonic() - phase_start
|
|
||||||
timings["connector_deletion_ttl"] = r.ttl(
|
|
||||||
OnyxRedisLocks.MONITOR_VESPA_SYNC_BEAT_LOCK
|
|
||||||
)
|
|
||||||
|
|
||||||
phase_start = time.monotonic()
|
|
||||||
lock_beat.reacquire()
|
|
||||||
for key_bytes in r_replica.scan_iter(
|
|
||||||
RedisDocumentSet.FENCE_PREFIX + "*", count=SCAN_ITER_COUNT_DEFAULT
|
|
||||||
):
|
|
||||||
if r.exists(key_bytes):
|
|
||||||
with get_session_with_tenant(tenant_id) as db_session:
|
with get_session_with_tenant(tenant_id) as db_session:
|
||||||
monitor_document_set_taskset(tenant_id, key_bytes, r, db_session)
|
monitor_document_set_taskset(tenant_id, key_bytes, r, db_session)
|
||||||
lock_beat.reacquire()
|
elif key_str.startswith(RedisUserGroup.FENCE_PREFIX):
|
||||||
timings["documentset"] = time.monotonic() - phase_start
|
|
||||||
timings["documentset_ttl"] = r.ttl(OnyxRedisLocks.MONITOR_VESPA_SYNC_BEAT_LOCK)
|
|
||||||
|
|
||||||
phase_start = time.monotonic()
|
|
||||||
lock_beat.reacquire()
|
|
||||||
for key_bytes in r_replica.scan_iter(
|
|
||||||
RedisUserGroup.FENCE_PREFIX + "*", count=SCAN_ITER_COUNT_DEFAULT
|
|
||||||
):
|
|
||||||
if r.exists(key_bytes):
|
|
||||||
monitor_usergroup_taskset = (
|
monitor_usergroup_taskset = (
|
||||||
fetch_versioned_implementation_with_fallback(
|
fetch_versioned_implementation_with_fallback(
|
||||||
"onyx.background.celery.tasks.vespa.tasks",
|
"onyx.background.celery.tasks.vespa.tasks",
|
||||||
@ -942,49 +928,21 @@ def monitor_vespa_sync(self: Task, tenant_id: str | None) -> bool | None:
|
|||||||
)
|
)
|
||||||
with get_session_with_tenant(tenant_id) as db_session:
|
with get_session_with_tenant(tenant_id) as db_session:
|
||||||
monitor_usergroup_taskset(tenant_id, key_bytes, r, db_session)
|
monitor_usergroup_taskset(tenant_id, key_bytes, r, db_session)
|
||||||
lock_beat.reacquire()
|
elif key_str.startswith(RedisConnectorDelete.FENCE_PREFIX):
|
||||||
timings["usergroup"] = time.monotonic() - phase_start
|
monitor_connector_deletion_taskset(tenant_id, key_bytes, r)
|
||||||
timings["usergroup_ttl"] = r.ttl(OnyxRedisLocks.MONITOR_VESPA_SYNC_BEAT_LOCK)
|
elif key_str.startswith(RedisConnectorPrune.FENCE_PREFIX):
|
||||||
|
|
||||||
phase_start = time.monotonic()
|
|
||||||
lock_beat.reacquire()
|
|
||||||
for key_bytes in r_replica.scan_iter(
|
|
||||||
RedisConnectorPrune.FENCE_PREFIX + "*", count=SCAN_ITER_COUNT_DEFAULT
|
|
||||||
):
|
|
||||||
if r.exists(key_bytes):
|
|
||||||
with get_session_with_tenant(tenant_id) as db_session:
|
with get_session_with_tenant(tenant_id) as db_session:
|
||||||
monitor_ccpair_pruning_taskset(tenant_id, key_bytes, r, db_session)
|
monitor_ccpair_pruning_taskset(tenant_id, key_bytes, r, db_session)
|
||||||
lock_beat.reacquire()
|
elif key_str.startswith(RedisConnectorIndex.FENCE_PREFIX):
|
||||||
timings["pruning"] = time.monotonic() - phase_start
|
|
||||||
timings["pruning_ttl"] = r.ttl(OnyxRedisLocks.MONITOR_VESPA_SYNC_BEAT_LOCK)
|
|
||||||
|
|
||||||
phase_start = time.monotonic()
|
|
||||||
lock_beat.reacquire()
|
|
||||||
for key_bytes in r_replica.scan_iter(
|
|
||||||
RedisConnectorIndex.FENCE_PREFIX + "*", count=SCAN_ITER_COUNT_DEFAULT
|
|
||||||
):
|
|
||||||
if r.exists(key_bytes):
|
|
||||||
with get_session_with_tenant(tenant_id) as db_session:
|
with get_session_with_tenant(tenant_id) as db_session:
|
||||||
monitor_ccpair_indexing_taskset(tenant_id, key_bytes, r, db_session)
|
monitor_ccpair_indexing_taskset(tenant_id, key_bytes, r, db_session)
|
||||||
lock_beat.reacquire()
|
elif key_str.startswith(RedisConnectorPermissionSync.FENCE_PREFIX):
|
||||||
timings["indexing"] = time.monotonic() - phase_start
|
|
||||||
timings["indexing_ttl"] = r.ttl(OnyxRedisLocks.MONITOR_VESPA_SYNC_BEAT_LOCK)
|
|
||||||
|
|
||||||
phase_start = time.monotonic()
|
|
||||||
lock_beat.reacquire()
|
|
||||||
for key_bytes in r_replica.scan_iter(
|
|
||||||
RedisConnectorPermissionSync.FENCE_PREFIX + "*",
|
|
||||||
count=SCAN_ITER_COUNT_DEFAULT,
|
|
||||||
):
|
|
||||||
if r.exists(key_bytes):
|
|
||||||
with get_session_with_tenant(tenant_id) as db_session:
|
with get_session_with_tenant(tenant_id) as db_session:
|
||||||
monitor_ccpair_permissions_taskset(
|
monitor_ccpair_permissions_taskset(
|
||||||
tenant_id, key_bytes, r, db_session
|
tenant_id, key_bytes, r, db_session
|
||||||
)
|
)
|
||||||
lock_beat.reacquire()
|
else:
|
||||||
|
pass
|
||||||
timings["permissions"] = time.monotonic() - phase_start
|
|
||||||
timings["permissions_ttl"] = r.ttl(OnyxRedisLocks.MONITOR_VESPA_SYNC_BEAT_LOCK)
|
|
||||||
except SoftTimeLimitExceeded:
|
except SoftTimeLimitExceeded:
|
||||||
task_logger.info(
|
task_logger.info(
|
||||||
"Soft time limit exceeded, task is being terminated gracefully."
|
"Soft time limit exceeded, task is being terminated gracefully."
|
||||||
@ -999,8 +957,8 @@ def monitor_vespa_sync(self: Task, tenant_id: str | None) -> bool | None:
|
|||||||
else:
|
else:
|
||||||
task_logger.error(
|
task_logger.error(
|
||||||
"monitor_vespa_sync - Lock not owned on completion: "
|
"monitor_vespa_sync - Lock not owned on completion: "
|
||||||
f"tenant={tenant_id} "
|
f"tenant={tenant_id}"
|
||||||
f"timings={timings}"
|
# f"timings={timings}"
|
||||||
)
|
)
|
||||||
redis_lock_dump(lock_beat, r)
|
redis_lock_dump(lock_beat, r)
|
||||||
|
|
||||||
@ -1064,15 +1022,6 @@ def vespa_metadata_sync_task(
|
|||||||
# the sync might repeat again later
|
# the sync might repeat again later
|
||||||
mark_document_as_synced(document_id, db_session)
|
mark_document_as_synced(document_id, db_session)
|
||||||
|
|
||||||
# this code checks for and removes a per document sync key that is
|
|
||||||
# used to block out the same doc from continualy resyncing
|
|
||||||
# a quick hack that is only needed for production issues
|
|
||||||
# redis_syncing_key = RedisConnectorCredentialPair.make_redis_syncing_key(
|
|
||||||
# document_id
|
|
||||||
# )
|
|
||||||
# r = get_redis_client(tenant_id=tenant_id)
|
|
||||||
# r.delete(redis_syncing_key)
|
|
||||||
|
|
||||||
elapsed = time.monotonic() - start
|
elapsed = time.monotonic() - start
|
||||||
task_logger.info(
|
task_logger.info(
|
||||||
f"doc={document_id} "
|
f"doc={document_id} "
|
||||||
@ -1114,3 +1063,23 @@ def vespa_metadata_sync_task(
|
|||||||
self.retry(exc=e, countdown=countdown)
|
self.retry(exc=e, countdown=countdown)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def is_fence(key_bytes: bytes) -> bool:
|
||||||
|
key_str = key_bytes.decode("utf-8")
|
||||||
|
if key_str == RedisGlobalConnectorCredentialPair.FENCE_KEY:
|
||||||
|
return True
|
||||||
|
if key_str.startswith(RedisDocumentSet.FENCE_PREFIX):
|
||||||
|
return True
|
||||||
|
if key_str.startswith(RedisUserGroup.FENCE_PREFIX):
|
||||||
|
return True
|
||||||
|
if key_str.startswith(RedisConnectorDelete.FENCE_PREFIX):
|
||||||
|
return True
|
||||||
|
if key_str.startswith(RedisConnectorPrune.FENCE_PREFIX):
|
||||||
|
return True
|
||||||
|
if key_str.startswith(RedisConnectorIndex.FENCE_PREFIX):
|
||||||
|
return True
|
||||||
|
if key_str.startswith(RedisConnectorPermissionSync.FENCE_PREFIX):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
@ -306,9 +306,18 @@ class OnyxRedisLocks:
|
|||||||
|
|
||||||
|
|
||||||
class OnyxRedisSignals:
|
class OnyxRedisSignals:
|
||||||
VALIDATE_INDEXING_FENCES = "signal:validate_indexing_fences"
|
BLOCK_VALIDATE_INDEXING_FENCES = "signal:block_validate_indexing_fences"
|
||||||
VALIDATE_EXTERNAL_GROUP_SYNC_FENCES = "signal:validate_external_group_sync_fences"
|
BLOCK_VALIDATE_EXTERNAL_GROUP_SYNC_FENCES = (
|
||||||
VALIDATE_PERMISSION_SYNC_FENCES = "signal:validate_permission_sync_fences"
|
"signal:block_validate_external_group_sync_fences"
|
||||||
|
)
|
||||||
|
BLOCK_VALIDATE_PERMISSION_SYNC_FENCES = (
|
||||||
|
"signal:block_validate_permission_sync_fences"
|
||||||
|
)
|
||||||
|
BLOCK_BUILD_FENCE_LOOKUP_TABLE = "signal:block_build_fence_lookup_table"
|
||||||
|
|
||||||
|
|
||||||
|
class OnyxRedisConstants:
|
||||||
|
ACTIVE_FENCES = "active_fences"
|
||||||
|
|
||||||
|
|
||||||
class OnyxCeleryPriority(int, Enum):
|
class OnyxCeleryPriority(int, Enum):
|
||||||
|
@ -17,6 +17,8 @@ class RedisConnector:
|
|||||||
associated background tasks / associated redis interactions."""
|
associated background tasks / associated redis interactions."""
|
||||||
|
|
||||||
def __init__(self, tenant_id: str | None, id: int) -> None:
|
def __init__(self, tenant_id: str | None, id: int) -> None:
|
||||||
|
"""id: a connector credential pair id"""
|
||||||
|
|
||||||
self.tenant_id: str | None = tenant_id
|
self.tenant_id: str | None = tenant_id
|
||||||
self.id: int = id
|
self.id: int = id
|
||||||
self.redis: redis.Redis = get_redis_client(tenant_id=tenant_id)
|
self.redis: redis.Redis = get_redis_client(tenant_id=tenant_id)
|
||||||
|
@ -2,6 +2,7 @@ import time
|
|||||||
from typing import cast
|
from typing import cast
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
|
import redis
|
||||||
from celery import Celery
|
from celery import Celery
|
||||||
from redis import Redis
|
from redis import Redis
|
||||||
from redis.lock import Lock as RedisLock
|
from redis.lock import Lock as RedisLock
|
||||||
@ -12,6 +13,7 @@ from onyx.configs.constants import CELERY_VESPA_SYNC_BEAT_LOCK_TIMEOUT
|
|||||||
from onyx.configs.constants import OnyxCeleryPriority
|
from onyx.configs.constants import OnyxCeleryPriority
|
||||||
from onyx.configs.constants import OnyxCeleryQueues
|
from onyx.configs.constants import OnyxCeleryQueues
|
||||||
from onyx.configs.constants import OnyxCeleryTask
|
from onyx.configs.constants import OnyxCeleryTask
|
||||||
|
from onyx.configs.constants import OnyxRedisConstants
|
||||||
from onyx.db.connector_credential_pair import get_connector_credential_pair_from_id
|
from onyx.db.connector_credential_pair import get_connector_credential_pair_from_id
|
||||||
from onyx.db.document import (
|
from onyx.db.document import (
|
||||||
construct_document_select_for_connector_credential_pair_by_needs_sync,
|
construct_document_select_for_connector_credential_pair_by_needs_sync,
|
||||||
@ -28,21 +30,14 @@ class RedisConnectorCredentialPair(RedisObjectHelper):
|
|||||||
all connectors and is not per connector."""
|
all connectors and is not per connector."""
|
||||||
|
|
||||||
PREFIX = "connectorsync"
|
PREFIX = "connectorsync"
|
||||||
FENCE_PREFIX = PREFIX + "_fence"
|
|
||||||
TASKSET_PREFIX = PREFIX + "_taskset"
|
TASKSET_PREFIX = PREFIX + "_taskset"
|
||||||
|
|
||||||
SYNCING_PREFIX = PREFIX + ":vespa_syncing"
|
|
||||||
|
|
||||||
def __init__(self, tenant_id: str | None, id: int) -> None:
|
def __init__(self, tenant_id: str | None, id: int) -> None:
|
||||||
super().__init__(tenant_id, str(id))
|
super().__init__(tenant_id, str(id))
|
||||||
|
|
||||||
# documents that should be skipped
|
# documents that should be skipped
|
||||||
self.skip_docs: set[str] = set()
|
self.skip_docs: set[str] = set()
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_fence_key(cls) -> str:
|
|
||||||
return RedisConnectorCredentialPair.FENCE_PREFIX
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_taskset_key(cls) -> str:
|
def get_taskset_key(cls) -> str:
|
||||||
return RedisConnectorCredentialPair.TASKSET_PREFIX
|
return RedisConnectorCredentialPair.TASKSET_PREFIX
|
||||||
@ -51,19 +46,14 @@ class RedisConnectorCredentialPair(RedisObjectHelper):
|
|||||||
def taskset_key(self) -> str:
|
def taskset_key(self) -> str:
|
||||||
"""Notice that this is intentionally reusing the same taskset for all
|
"""Notice that this is intentionally reusing the same taskset for all
|
||||||
connector syncs"""
|
connector syncs"""
|
||||||
# example: connector_taskset
|
# example: connectorsync_taskset
|
||||||
return f"{self.TASKSET_PREFIX}"
|
return f"{self.TASKSET_PREFIX}"
|
||||||
|
|
||||||
def set_skip_docs(self, skip_docs: set[str]) -> None:
|
def set_skip_docs(self, skip_docs: set[str]) -> None:
|
||||||
# documents that should be skipped. Note that this classes updates
|
# documents that should be skipped. Note that this class updates
|
||||||
# the list on the fly
|
# the list on the fly
|
||||||
self.skip_docs = skip_docs
|
self.skip_docs = skip_docs
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def make_redis_syncing_key(doc_id: str) -> str:
|
|
||||||
"""used to create a key in redis to block a doc from syncing"""
|
|
||||||
return f"{RedisConnectorCredentialPair.SYNCING_PREFIX}:{doc_id}"
|
|
||||||
|
|
||||||
def generate_tasks(
|
def generate_tasks(
|
||||||
self,
|
self,
|
||||||
max_tasks: int,
|
max_tasks: int,
|
||||||
@ -111,15 +101,6 @@ class RedisConnectorCredentialPair(RedisObjectHelper):
|
|||||||
if doc.id in self.skip_docs:
|
if doc.id in self.skip_docs:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# an arbitrary number in seconds to prevent the same doc from syncing repeatedly
|
|
||||||
# SYNC_EXPIRATION = 24 * 60 * 60
|
|
||||||
|
|
||||||
# a quick hack that can be uncommented to prevent a doc from resyncing over and over
|
|
||||||
# redis_syncing_key = self.make_redis_syncing_key(doc.id)
|
|
||||||
# if redis_client.exists(redis_syncing_key):
|
|
||||||
# continue
|
|
||||||
# redis_client.set(redis_syncing_key, custom_task_id, ex=SYNC_EXPIRATION)
|
|
||||||
|
|
||||||
# celery's default task id format is "dd32ded3-00aa-4884-8b21-42f8332e7fac"
|
# celery's default task id format is "dd32ded3-00aa-4884-8b21-42f8332e7fac"
|
||||||
# the key for the result is "celery-task-meta-dd32ded3-00aa-4884-8b21-42f8332e7fac"
|
# the key for the result is "celery-task-meta-dd32ded3-00aa-4884-8b21-42f8332e7fac"
|
||||||
# we prefix the task id so it's easier to keep track of who created the task
|
# we prefix the task id so it's easier to keep track of who created the task
|
||||||
@ -148,3 +129,78 @@ class RedisConnectorCredentialPair(RedisObjectHelper):
|
|||||||
break
|
break
|
||||||
|
|
||||||
return len(async_results), num_docs
|
return len(async_results), num_docs
|
||||||
|
|
||||||
|
|
||||||
|
class RedisGlobalConnectorCredentialPair:
|
||||||
|
"""This class is used to scan documents by cc_pair in the db and collect them into
|
||||||
|
a unified set for syncing.
|
||||||
|
|
||||||
|
It differs from the other redis helpers in that the taskset used spans
|
||||||
|
all connectors and is not per connector."""
|
||||||
|
|
||||||
|
PREFIX = "connectorsync"
|
||||||
|
FENCE_KEY = PREFIX + "_fence"
|
||||||
|
TASKSET_KEY = PREFIX + "_taskset"
|
||||||
|
|
||||||
|
def __init__(self, redis: redis.Redis) -> None:
|
||||||
|
self.redis = redis
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fenced(self) -> bool:
|
||||||
|
if self.redis.exists(self.fence_key):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def payload(self) -> int | None:
|
||||||
|
bytes = self.redis.get(self.fence_key)
|
||||||
|
if bytes is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
progress = int(cast(int, bytes))
|
||||||
|
return progress
|
||||||
|
|
||||||
|
def get_remaining(self) -> int:
|
||||||
|
remaining = cast(int, self.redis.scard(self.taskset_key))
|
||||||
|
return remaining
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fence_key(self) -> str:
|
||||||
|
"""Notice that this is intentionally reusing the same fence for all
|
||||||
|
connector syncs"""
|
||||||
|
# example: connectorsync_fence
|
||||||
|
return f"{self.FENCE_KEY}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def taskset_key(self) -> str:
|
||||||
|
"""Notice that this is intentionally reusing the same taskset for all
|
||||||
|
connector syncs"""
|
||||||
|
# example: connectorsync_taskset
|
||||||
|
return f"{self.TASKSET_KEY}"
|
||||||
|
|
||||||
|
def set_fence(self, payload: int | None) -> None:
|
||||||
|
if payload is None:
|
||||||
|
self.redis.srem(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
|
self.redis.delete(self.fence_key)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.redis.set(self.fence_key, payload)
|
||||||
|
self.redis.sadd(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
|
|
||||||
|
def delete_taskset(self) -> None:
|
||||||
|
self.redis.delete(self.taskset_key)
|
||||||
|
|
||||||
|
def reset(self) -> None:
|
||||||
|
self.redis.srem(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
|
self.redis.delete(self.taskset_key)
|
||||||
|
self.redis.delete(self.fence_key)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def reset_all(r: redis.Redis) -> None:
|
||||||
|
r.srem(
|
||||||
|
OnyxRedisConstants.ACTIVE_FENCES,
|
||||||
|
RedisGlobalConnectorCredentialPair.FENCE_KEY,
|
||||||
|
)
|
||||||
|
r.delete(RedisGlobalConnectorCredentialPair.TASKSET_KEY)
|
||||||
|
r.delete(RedisGlobalConnectorCredentialPair.FENCE_KEY)
|
||||||
|
@ -14,6 +14,7 @@ from onyx.configs.constants import CELERY_VESPA_SYNC_BEAT_LOCK_TIMEOUT
|
|||||||
from onyx.configs.constants import OnyxCeleryPriority
|
from onyx.configs.constants import OnyxCeleryPriority
|
||||||
from onyx.configs.constants import OnyxCeleryQueues
|
from onyx.configs.constants import OnyxCeleryQueues
|
||||||
from onyx.configs.constants import OnyxCeleryTask
|
from onyx.configs.constants import OnyxCeleryTask
|
||||||
|
from onyx.configs.constants import OnyxRedisConstants
|
||||||
from onyx.db.connector_credential_pair import get_connector_credential_pair_from_id
|
from onyx.db.connector_credential_pair import get_connector_credential_pair_from_id
|
||||||
from onyx.db.document import construct_document_select_for_connector_credential_pair
|
from onyx.db.document import construct_document_select_for_connector_credential_pair
|
||||||
from onyx.db.models import Document as DbDocument
|
from onyx.db.models import Document as DbDocument
|
||||||
@ -69,10 +70,12 @@ class RedisConnectorDelete:
|
|||||||
|
|
||||||
def set_fence(self, payload: RedisConnectorDeletePayload | None) -> None:
|
def set_fence(self, payload: RedisConnectorDeletePayload | None) -> None:
|
||||||
if not payload:
|
if not payload:
|
||||||
|
self.redis.srem(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
self.redis.delete(self.fence_key)
|
self.redis.delete(self.fence_key)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.redis.set(self.fence_key, payload.model_dump_json())
|
self.redis.set(self.fence_key, payload.model_dump_json())
|
||||||
|
self.redis.sadd(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
|
|
||||||
def _generate_task_id(self) -> str:
|
def _generate_task_id(self) -> str:
|
||||||
# celery's default task id format is "dd32ded3-00aa-4884-8b21-42f8332e7fac"
|
# celery's default task id format is "dd32ded3-00aa-4884-8b21-42f8332e7fac"
|
||||||
@ -136,6 +139,7 @@ class RedisConnectorDelete:
|
|||||||
return len(async_results)
|
return len(async_results)
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self) -> None:
|
||||||
|
self.redis.srem(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
self.redis.delete(self.taskset_key)
|
self.redis.delete(self.taskset_key)
|
||||||
self.redis.delete(self.fence_key)
|
self.redis.delete(self.fence_key)
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ from onyx.configs.constants import CELERY_GENERIC_BEAT_LOCK_TIMEOUT
|
|||||||
from onyx.configs.constants import OnyxCeleryPriority
|
from onyx.configs.constants import OnyxCeleryPriority
|
||||||
from onyx.configs.constants import OnyxCeleryQueues
|
from onyx.configs.constants import OnyxCeleryQueues
|
||||||
from onyx.configs.constants import OnyxCeleryTask
|
from onyx.configs.constants import OnyxCeleryTask
|
||||||
|
from onyx.configs.constants import OnyxRedisConstants
|
||||||
from onyx.redis.redis_pool import SCAN_ITER_COUNT_DEFAULT
|
from onyx.redis.redis_pool import SCAN_ITER_COUNT_DEFAULT
|
||||||
|
|
||||||
|
|
||||||
@ -111,10 +112,12 @@ class RedisConnectorPermissionSync:
|
|||||||
payload: RedisConnectorPermissionSyncPayload | None,
|
payload: RedisConnectorPermissionSyncPayload | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
if not payload:
|
if not payload:
|
||||||
|
self.redis.srem(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
self.redis.delete(self.fence_key)
|
self.redis.delete(self.fence_key)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.redis.set(self.fence_key, payload.model_dump_json())
|
self.redis.set(self.fence_key, payload.model_dump_json())
|
||||||
|
self.redis.sadd(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
|
|
||||||
def set_active(self) -> None:
|
def set_active(self) -> None:
|
||||||
"""This sets a signal to keep the permissioning flow from getting cleaned up within
|
"""This sets a signal to keep the permissioning flow from getting cleaned up within
|
||||||
@ -196,6 +199,7 @@ class RedisConnectorPermissionSync:
|
|||||||
return len(async_results)
|
return len(async_results)
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self) -> None:
|
||||||
|
self.redis.srem(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
self.redis.delete(self.active_key)
|
self.redis.delete(self.active_key)
|
||||||
self.redis.delete(self.generator_progress_key)
|
self.redis.delete(self.generator_progress_key)
|
||||||
self.redis.delete(self.generator_complete_key)
|
self.redis.delete(self.generator_complete_key)
|
||||||
|
@ -5,6 +5,8 @@ from uuid import uuid4
|
|||||||
import redis
|
import redis
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from onyx.configs.constants import OnyxRedisConstants
|
||||||
|
|
||||||
|
|
||||||
class RedisConnectorIndexPayload(BaseModel):
|
class RedisConnectorIndexPayload(BaseModel):
|
||||||
index_attempt_id: int | None
|
index_attempt_id: int | None
|
||||||
@ -103,10 +105,12 @@ class RedisConnectorIndex:
|
|||||||
payload: RedisConnectorIndexPayload | None,
|
payload: RedisConnectorIndexPayload | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
if not payload:
|
if not payload:
|
||||||
|
self.redis.srem(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
self.redis.delete(self.fence_key)
|
self.redis.delete(self.fence_key)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.redis.set(self.fence_key, payload.model_dump_json())
|
self.redis.set(self.fence_key, payload.model_dump_json())
|
||||||
|
self.redis.sadd(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
|
|
||||||
def terminating(self, celery_task_id: str) -> bool:
|
def terminating(self, celery_task_id: str) -> bool:
|
||||||
if self.redis.exists(f"{self.terminate_key}_{celery_task_id}"):
|
if self.redis.exists(f"{self.terminate_key}_{celery_task_id}"):
|
||||||
@ -188,6 +192,7 @@ class RedisConnectorIndex:
|
|||||||
return status
|
return status
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self) -> None:
|
||||||
|
self.redis.srem(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
self.redis.delete(self.active_key)
|
self.redis.delete(self.active_key)
|
||||||
self.redis.delete(self.generator_lock_key)
|
self.redis.delete(self.generator_lock_key)
|
||||||
self.redis.delete(self.generator_progress_key)
|
self.redis.delete(self.generator_progress_key)
|
||||||
|
@ -11,6 +11,7 @@ from onyx.configs.constants import CELERY_GENERIC_BEAT_LOCK_TIMEOUT
|
|||||||
from onyx.configs.constants import OnyxCeleryPriority
|
from onyx.configs.constants import OnyxCeleryPriority
|
||||||
from onyx.configs.constants import OnyxCeleryQueues
|
from onyx.configs.constants import OnyxCeleryQueues
|
||||||
from onyx.configs.constants import OnyxCeleryTask
|
from onyx.configs.constants import OnyxCeleryTask
|
||||||
|
from onyx.configs.constants import OnyxRedisConstants
|
||||||
from onyx.db.connector_credential_pair import get_connector_credential_pair_from_id
|
from onyx.db.connector_credential_pair import get_connector_credential_pair_from_id
|
||||||
from onyx.redis.redis_pool import SCAN_ITER_COUNT_DEFAULT
|
from onyx.redis.redis_pool import SCAN_ITER_COUNT_DEFAULT
|
||||||
|
|
||||||
@ -79,10 +80,12 @@ class RedisConnectorPrune:
|
|||||||
|
|
||||||
def set_fence(self, value: bool) -> None:
|
def set_fence(self, value: bool) -> None:
|
||||||
if not value:
|
if not value:
|
||||||
|
self.redis.srem(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
self.redis.delete(self.fence_key)
|
self.redis.delete(self.fence_key)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.redis.set(self.fence_key, 0)
|
self.redis.set(self.fence_key, 0)
|
||||||
|
self.redis.sadd(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def generator_complete(self) -> int | None:
|
def generator_complete(self) -> int | None:
|
||||||
@ -158,6 +161,7 @@ class RedisConnectorPrune:
|
|||||||
return len(async_results)
|
return len(async_results)
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self) -> None:
|
||||||
|
self.redis.srem(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
self.redis.delete(self.generator_progress_key)
|
self.redis.delete(self.generator_progress_key)
|
||||||
self.redis.delete(self.generator_complete_key)
|
self.redis.delete(self.generator_complete_key)
|
||||||
self.redis.delete(self.taskset_key)
|
self.redis.delete(self.taskset_key)
|
||||||
|
@ -13,6 +13,7 @@ from onyx.configs.constants import CELERY_VESPA_SYNC_BEAT_LOCK_TIMEOUT
|
|||||||
from onyx.configs.constants import OnyxCeleryPriority
|
from onyx.configs.constants import OnyxCeleryPriority
|
||||||
from onyx.configs.constants import OnyxCeleryQueues
|
from onyx.configs.constants import OnyxCeleryQueues
|
||||||
from onyx.configs.constants import OnyxCeleryTask
|
from onyx.configs.constants import OnyxCeleryTask
|
||||||
|
from onyx.configs.constants import OnyxRedisConstants
|
||||||
from onyx.db.document_set import construct_document_select_by_docset
|
from onyx.db.document_set import construct_document_select_by_docset
|
||||||
from onyx.db.models import Document
|
from onyx.db.models import Document
|
||||||
from onyx.redis.redis_object_helper import RedisObjectHelper
|
from onyx.redis.redis_object_helper import RedisObjectHelper
|
||||||
@ -35,10 +36,12 @@ class RedisDocumentSet(RedisObjectHelper):
|
|||||||
|
|
||||||
def set_fence(self, payload: int | None) -> None:
|
def set_fence(self, payload: int | None) -> None:
|
||||||
if payload is None:
|
if payload is None:
|
||||||
|
self.redis.srem(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
self.redis.delete(self.fence_key)
|
self.redis.delete(self.fence_key)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.redis.set(self.fence_key, payload)
|
self.redis.set(self.fence_key, payload)
|
||||||
|
self.redis.sadd(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def payload(self) -> int | None:
|
def payload(self) -> int | None:
|
||||||
@ -96,6 +99,7 @@ class RedisDocumentSet(RedisObjectHelper):
|
|||||||
return len(async_results), len(async_results)
|
return len(async_results), len(async_results)
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self) -> None:
|
||||||
|
self.redis.srem(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
self.redis.delete(self.taskset_key)
|
self.redis.delete(self.taskset_key)
|
||||||
self.redis.delete(self.fence_key)
|
self.redis.delete(self.fence_key)
|
||||||
|
|
||||||
|
@ -113,6 +113,8 @@ class TenantRedis(redis.Redis):
|
|||||||
"reacquire",
|
"reacquire",
|
||||||
"create_lock",
|
"create_lock",
|
||||||
"startswith",
|
"startswith",
|
||||||
|
"smembers",
|
||||||
|
"sismember",
|
||||||
"sadd",
|
"sadd",
|
||||||
"srem",
|
"srem",
|
||||||
"scard",
|
"scard",
|
||||||
|
@ -13,6 +13,7 @@ from onyx.configs.constants import CELERY_VESPA_SYNC_BEAT_LOCK_TIMEOUT
|
|||||||
from onyx.configs.constants import OnyxCeleryPriority
|
from onyx.configs.constants import OnyxCeleryPriority
|
||||||
from onyx.configs.constants import OnyxCeleryQueues
|
from onyx.configs.constants import OnyxCeleryQueues
|
||||||
from onyx.configs.constants import OnyxCeleryTask
|
from onyx.configs.constants import OnyxCeleryTask
|
||||||
|
from onyx.configs.constants import OnyxRedisConstants
|
||||||
from onyx.db.models import Document
|
from onyx.db.models import Document
|
||||||
from onyx.redis.redis_object_helper import RedisObjectHelper
|
from onyx.redis.redis_object_helper import RedisObjectHelper
|
||||||
from onyx.utils.variable_functionality import fetch_versioned_implementation
|
from onyx.utils.variable_functionality import fetch_versioned_implementation
|
||||||
@ -36,10 +37,12 @@ class RedisUserGroup(RedisObjectHelper):
|
|||||||
|
|
||||||
def set_fence(self, payload: int | None) -> None:
|
def set_fence(self, payload: int | None) -> None:
|
||||||
if payload is None:
|
if payload is None:
|
||||||
|
self.redis.srem(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
self.redis.delete(self.fence_key)
|
self.redis.delete(self.fence_key)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.redis.set(self.fence_key, payload)
|
self.redis.set(self.fence_key, payload)
|
||||||
|
self.redis.sadd(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def payload(self) -> int | None:
|
def payload(self) -> int | None:
|
||||||
@ -109,6 +112,7 @@ class RedisUserGroup(RedisObjectHelper):
|
|||||||
return len(async_results), len(async_results)
|
return len(async_results), len(async_results)
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self) -> None:
|
||||||
|
self.redis.srem(OnyxRedisConstants.ACTIVE_FENCES, self.fence_key)
|
||||||
self.redis.delete(self.taskset_key)
|
self.redis.delete(self.taskset_key)
|
||||||
self.redis.delete(self.fence_key)
|
self.redis.delete(self.fence_key)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user