Skip to content

Commit

Permalink
fix(backup): Gracefully handle deleted models in compare tool (getsen…
Browse files Browse the repository at this point in the history
  • Loading branch information
azaslavsky authored May 30, 2024
1 parent c8ded8a commit e2e740f
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 31 deletions.
35 changes: 35 additions & 0 deletions src/sentry/backup/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,41 @@
from sentry.silo.base import SiloMode
from sentry.utils import json

# We have to be careful when removing fields from our model schemas, since exports created using
# the old-but-still-in-the-support-window versions could have those fields set in the data they
# provide. This dict serves as a map of all fields that have been deleted on HEAD but are still
# valid in at least one of the versions we support. For example, since our current version
# support window is two minor versions back, if we delete a field at version 24.5.N, we must
# include an entry in this map for that field until that version is out of the support window
# (in this case, we can remove shim once version 24.7.0 is released).
#
# NOTE TO FUTURE EDITORS: please keep the `DELETED_FIELDS` dict, and the subsequent `if` clause,
# around even if the dict is empty, to ensure that there is a ready place to pop shims into. For
# each entry in this dict, please leave a TODO comment pointed to a github issue for removing
# the shim, noting in the comment which self-hosted release will trigger the removal.
DELETED_FIELDS: dict[str, set[str]] = {
# TODO(mark): Safe to remove after july 2024 after self-hosted 24.6.0 is released
"sentry.team": {"actor"},
# TODO(mark): Safe to remove after july 2024 after self-hosted 24.6.0 is released
"sentry.rule": {"owner"},
# TODO(mark): Safe to remove after july 2024 after self-hosted 24.6.0 is released
"sentry.alertrule": {"owner"},
# TODO(mark): Safe to remove after july 2024 after self-hosted 24.6.0 is released
"sentry.grouphistory": {"actor"},
}

# When models are removed from the application, they will continue to be in exports
# from previous releases. Models in this list are elided from data as imports are processed.
#
# NOTE TO FUTURE EDITORS: please keep the `DELETED_MODELS` set, and the subsequent `if` clause,
# around even if the set is empty, to ensure that there is a ready place to pop shims into. For
# each entry in this set, please leave a TODO comment pointed to a github issue for removing
# the shim, noting in the comment which self-hosted release will trigger the removal.
DELETED_MODELS = {
# TODO(mark): Safe to remove after july 2024 after self-hosted 24.6.0 is released
"sentry.actor"
}


class NormalizedModelName:
"""
Expand Down
34 changes: 4 additions & 30 deletions src/sentry/backup/imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

from sentry.backup.crypto import Decryptor, decrypt_encrypted_tarball
from sentry.backup.dependencies import (
DELETED_FIELDS,
DELETED_MODELS,
ImportKind,
ModelRelations,
NormalizedModelName,
Expand Down Expand Up @@ -53,33 +55,6 @@

logger = logging.getLogger(__name__)

# We have to be careful when removing fields from our model schemas, since exports created using
# the old-but-still-in-the-support-window versions could have those fields set in the data they
# provide. This dict serves as a map of all fields that have been deleted on HEAD but are still
# valid in at least one of the versions we support. For example, since our current version
# support window is two minor versions back, if we delete a field at version 24.5.N, we must
# include an entry in this map for that field until that version is out of the support window
# (in this case, we can remove shim once version 24.7.0 is released).
#
# NOTE TO FUTURE EDITORS: please keep the `DELETED_FIELDS` dict, and the subsequent `if` clause,
# around even if the dict is empty, to ensure that there is a ready place to pop shims into. For
# each entry in this dict, please leave a TODO comment pointed to a github issue for removing
# the shim, noting in the comment which self-hosted release will trigger the removal.
DELETED_FIELDS: dict[str, set[str]] = {
# The actor field should be retained until 24.6.0
"sentry.team": {"actor"},
# TODO(mark): Safe to remove after july 2024 after self-hosted 24.6.0 is released
"sentry.rule": {"owner"},
# TODO(mark): Safe to remove after july 2024 after self-hosted 24.6.0 is released
"sentry.alertrule": {"owner"},
# TODO(mark): Safe to remove after july 2024 after self-hosted 24.6.0 is released
"sentry.grouphistory": {"actor"},
}

# When models are removed from the application, they will continue to be in exports
# from previous releases. Models in this list are elided from data as imports are processed.
DELETED_MODELS = {"sentry.actor"}

# The maximum number of models that may be sent at a time.
MAX_BATCH_SIZE = 20

Expand Down Expand Up @@ -179,10 +154,9 @@ def _import(
)

if len(DELETED_MODELS) > 0 or len(DELETED_FIELDS) > 0:
# Parse the content JSON and remove fields and models that we have marked for deletion in the
# function.
# Parse the content JSON and remove fields and models that we have marked for deletion in
# the function.
content_as_json = orjson.loads(content)

shimmed_models = set(DELETED_FIELDS.keys())
for i, json_model in enumerate(content_as_json):
if json_model["model"] in shimmed_models:
Expand Down
10 changes: 9 additions & 1 deletion src/sentry/backup/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@
from typing import Any

from sentry.backup.comparators import ComparatorMap, ForeignKeyComparator, get_default_comparators
from sentry.backup.dependencies import ImportKind, NormalizedModelName, PrimaryKeyMap, get_model
from sentry.backup.dependencies import (
DELETED_MODELS,
ImportKind,
NormalizedModelName,
PrimaryKeyMap,
get_model,
)
from sentry.backup.findings import (
ComparatorFinding,
ComparatorFindingKind,
Expand Down Expand Up @@ -96,6 +102,8 @@ def build_model_map(
pk = model["pk"]
model_name = NormalizedModelName(model["model"])
model_type = get_model(model_name)
if str(model_name) in DELETED_MODELS:
continue
if model_type is None or not issubclass(model_type, BaseModel):
raise RuntimeError("Unknown model class")

Expand Down

0 comments on commit e2e740f

Please sign in to comment.