Skip to content

Commit 54369ef

Browse files
authored
fix(backup): Apply prod-UserOption fix to comparison (#69460)
The fixes in #68943 and #69027 worked, but now fail on comparison. This PR makes comparison clever enough to detect these "duplicate" UserOption entries and avoid them. This is a temporary fix. A more robust solution will prevent ever exporting such UserOptions, but that will take a while to percolate through the support window, so this should hold us over for the time being.
1 parent 58574a4 commit 54369ef

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed

src/sentry/backup/validate.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ def build_model_map(
9191
need_ordering: dict[NormalizedModelName, dict[tuple, JSONData]] = defaultdict(dict)
9292
pks_to_usernames: dict[int, str] = dict()
9393

94+
# TODO(azaslavsky): This is a temporary fix. Remove it once we have a more
95+
# sustainable export-side solution.
96+
user_options_seen_ids: dict[tuple[int, str], InstanceID] = dict()
97+
9498
for model in models:
9599
pk = model["pk"]
96100
model_name = NormalizedModelName(model["model"])
@@ -106,6 +110,20 @@ def build_model_map(
106110
id, found = ordinal_counters[model_name].assign(model, pk, side)
107111
findings.extend(found)
108112
model_map[model_name][id] = model
113+
114+
# TODO(azaslavsky): This is a temporary fix. Remove it once we have a more
115+
# sustainable export-side solution.
116+
if (
117+
str(model_name) == "sentry.useroption"
118+
and not model["fields"].get("project_id", None)
119+
and not model["fields"].get("organization_id", None)
120+
):
121+
duplicate_id = user_options_seen_ids.pop(
122+
(model["fields"]["user"], model["fields"]["key"]), None
123+
)
124+
user_options_seen_ids[(model["fields"]["user"], model["fields"]["key"])] = id
125+
if duplicate_id is not None:
126+
del model_map[model_name][duplicate_id]
109127
continue
110128

111129
custom_ordinal_parts = []
@@ -139,6 +157,18 @@ def build_model_map(
139157
findings.extend(found)
140158
model_map[model_name][id] = model
141159

160+
# TODO(azaslavsky): This is a temporary fix. Remove it once we have a more sustainable
161+
# export-side solution.
162+
user_option_model_name = NormalizedModelName("sentry.useroption")
163+
user_option_model_map = model_map.get(user_option_model_name, None)
164+
if user_option_model_map is not None:
165+
counter = 1
166+
model_map[user_option_model_name] = ordereddict()
167+
for model in user_option_model_map.values():
168+
id = InstanceID(str(user_option_model_name), counter)
169+
model_map[user_option_model_name][id] = model
170+
counter += 1
171+
142172
return (model_map, ordinal_counters)
143173

144174
def json_lines(obj: JSONData) -> list[str]:

tests/sentry/backup/test_validate.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,3 +792,101 @@ def test_good_user_custom_ordinal():
792792
findings = out.findings
793793

794794
assert len(findings) == 0
795+
796+
797+
# TODO(azaslavsky): This is a temporary fix. Remove it once we have a more sustainable export-side
798+
# solution.
799+
def test_good_user_option_temp_fix():
800+
left = json.loads(
801+
"""
802+
[
803+
{
804+
"model": "sentry.user",
805+
"pk": 1,
806+
"fields": {
807+
"password": "abc123",
808+
"last_login": null,
809+
"username": "testing@example.com",
810+
"name": "",
811+
"email": "testing@example.com"
812+
}
813+
},
814+
{
815+
"model": "sentry.useroption",
816+
"pk": 3,
817+
"fields": {
818+
"user": 1,
819+
"key": "foo",
820+
"value": "1"
821+
}
822+
},
823+
{
824+
"model": "sentry.useroption",
825+
"pk": 5,
826+
"fields": {
827+
"user": 1,
828+
"key": "foo",
829+
"value": "2"
830+
}
831+
},
832+
{
833+
"model": "sentry.useroption",
834+
"pk": 8,
835+
"fields": {
836+
"user": 1,
837+
"key": "bar",
838+
"value": "4"
839+
}
840+
}
841+
]
842+
"""
843+
)
844+
845+
right = json.loads(
846+
"""
847+
[
848+
{
849+
"model": "sentry.user",
850+
"pk": 1,
851+
"fields": {
852+
"password": "abc123",
853+
"last_login": null,
854+
"username": "testing@example.com",
855+
"name": "",
856+
"email": "testing@example.com"
857+
}
858+
},
859+
{
860+
"model": "sentry.useroption",
861+
"pk": 11,
862+
"fields": {
863+
"user": 1,
864+
"key": "foo",
865+
"value": "2"
866+
}
867+
},
868+
{
869+
"model": "sentry.useroption",
870+
"pk": 21,
871+
"fields": {
872+
"user": 1,
873+
"key": "bar",
874+
"value": "3"
875+
}
876+
},
877+
{
878+
"model": "sentry.useroption",
879+
"pk": 22,
880+
"fields": {
881+
"user": 1,
882+
"key": "bar",
883+
"value": "4"
884+
}
885+
}
886+
]
887+
"""
888+
)
889+
out = validate(left, right)
890+
findings = out.findings
891+
892+
assert len(findings) == 0

0 commit comments

Comments
 (0)