def update_config_cache(generate, organization_id=None, project_id=None, update_reason=None): """ Update the Redis cache for the Relay projectconfig. This task is invoked whenever a project/org option has been saved or smart quotas potentially caused a change in projectconfig. Either organization_id or project_id has to be provided. :param organization_id: The organization for which to invalidate configs. :param project_id: The project for which to invalidate configs. :param generate: If `True`, caches will be eagerly regenerated, not only invalidated. """ from sentry.models import Project, ProjectKey, ProjectKeyStatus from sentry.relay import projectconfig_cache from sentry.relay.config import get_project_config if project_id: set_current_event_project(project_id) if organization_id: # Cannot use bind_organization_context here because we do not have a # model and don't want to fetch one sentry_sdk.set_tag("organization_id", organization_id) sentry_sdk.set_tag("update_reason", update_reason) sentry_sdk.set_tag("generate", generate) # Delete key before generating configs such that we never have an outdated # but valid cache. # # If this was running at the end of the task, it would be more effective # against bursts of updates, but introduces a different race where an # outdated cache may be used. projectconfig_debounce_cache.mark_task_done(project_id, organization_id) if project_id: projects = [Project.objects.get_from_cache(id=project_id)] elif organization_id: # XXX(markus): I feel like we should be able to cache this but I don't # want to add another method to src/sentry/db/models/manager.py projects = Project.objects.filter(organization_id=organization_id) project_keys = {} for key in ProjectKey.objects.filter( project_id__in=[project.id for project in projects]): project_keys.setdefault(key.project_id, []).append(key) if generate: config_cache = {} for project in projects: project_config = get_project_config(project, project_keys=project_keys.get( project.id, []), full_config=True) config_cache[project.id] = project_config.to_dict() for key in project_keys.get(project.id) or (): # XXX(markus): This is currently the cleanest way to get only # state for a single projectkey (considering quotas and # everything) if key.status != ProjectKeyStatus.ACTIVE: continue project_config = get_project_config(project, project_keys=[key], full_config=True) config_cache[key.public_key] = project_config.to_dict() projectconfig_cache.set_many(config_cache) else: cache_keys_to_delete = [] for project in projects: cache_keys_to_delete.append(project.id) for key in project_keys.get(project.id) or (): cache_keys_to_delete.append(key.public_key) projectconfig_cache.delete_many(cache_keys_to_delete) metrics.incr( "relay.projectconfig_cache.done", tags={ "generate": generate, "update_reason": update_reason }, )
def update_config_cache(generate, organization_id=None, project_id=None, public_key=None, update_reason=None): """ Update the Redis cache for the Relay projectconfig. This task is invoked whenever a project/org option has been saved or smart quotas potentially caused a change in projectconfig. Either organization_id or project_id has to be provided. :param organization_id: The organization for which to invalidate configs. :param project_id: The project for which to invalidate configs. :param generate: If `True`, caches will be eagerly regenerated, not only invalidated. """ from sentry.models import Project, ProjectKey, ProjectKeyStatus from sentry.relay import projectconfig_cache from sentry.relay.config import get_project_config if project_id: set_current_event_project(project_id) if organization_id: # Cannot use bind_organization_context here because we do not have a # model and don't want to fetch one sentry_sdk.set_tag("organization_id", organization_id) if public_key: sentry_sdk.set_tag("public_key", public_key) sentry_sdk.set_tag("update_reason", update_reason) sentry_sdk.set_tag("generate", generate) # Delete key before generating configs such that we never have an outdated # but valid cache. # # If this was running at the end of the task, it would be more effective # against bursts of updates, but introduces a different race where an # outdated cache may be used. projectconfig_debounce_cache.mark_task_done(public_key, project_id, organization_id) if organization_id: projects = list( Project.objects.filter(organization_id=organization_id)) keys = list(ProjectKey.objects.filter(project__in=projects)) elif project_id: projects = [Project.objects.get(id=project_id)] keys = list(ProjectKey.objects.filter(project__in=projects)) elif public_key: try: keys = [ProjectKey.objects.get(public_key=public_key)] except ProjectKey.DoesNotExist: # In this particular case, where a project key got deleted and # triggered an update, we at least know the public key that needs # to be deleted from cache. # # In other similar cases, like an org being deleted, we potentially # cannot find any keys anymore, so we don't know which cache keys # to delete. projectconfig_cache.delete_many([public_key]) return else: assert False if generate: config_cache = {} for key in keys: if key.status != ProjectKeyStatus.ACTIVE: project_config = {"disabled": True} else: project_config = get_project_config( key.project, project_keys=[key], full_config=True).to_dict() config_cache[key.public_key] = project_config projectconfig_cache.set_many(config_cache) else: cache_keys_to_delete = [] for key in keys: cache_keys_to_delete.append(key.public_key) projectconfig_cache.delete_many(cache_keys_to_delete)
def update_config_cache(generate, organization_id=None, project_id=None, update_reason=None): """ Update the Redis cache for the Relay projectconfig. This task is invoked whenever a project/org option has been saved or smart quotas potentially caused a change in projectconfig. Either organization_id or project_id has to be provided. :param organization_id: The organization for which to invalidate configs. :param project_id: The project for which to invalidate configs. :param generate: If `True`, caches will be eagerly regenerated, not only invalidated. """ from sentry.models import Project from sentry.relay import projectconfig_cache from sentry.relay.config import get_project_config # Delete key before generating configs such that we never have an outdated # but valid cache. # # If this was running at the end of the task, it would be more effective # against bursts of updates, but introduces a different race where an # outdated cache may be used. projectconfig_debounce_cache.mark_task_done(project_id, organization_id) if project_id: projects = [Project.objects.get_from_cache(id=project_id)] elif organization_id: # XXX(markus): I feel like we should be able to cache this but I don't # want to add another method to src/sentry/db/models/manager.py projects = Project.objects.filter(organization_id=organization_id) if generate: project_keys = {} for key in ProjectKey.objects.filter( project_id__in=[project.id for project in projects]): project_keys.setdefault(key.project_id, []).append(key) project_configs = {} for project in projects: project_config = get_project_config(project, project_keys=project_keys.get( project.id, []), full_config=True) project_configs[project.id] = project_config.to_dict() projectconfig_cache.set_many(project_configs) else: projectconfig_cache.delete_many([project.id for project in projects]) metrics.incr( "relay.projectconfig_cache.done", tags={ "generate": generate, "update_reason": update_reason }, )