Add aliasing for table of placeholder contribution details, to make resilient to PG schema changes
About
In #443554 (closed) we are adding a table to save details of placeholder contributions. Its design involves saving model
and column
(name) data.
Problem
This is brittle as we persist names PostgreSQL schema that are changeable. A model or column name change could lead to us failing to assign user contributions.
Proposal
Treat the model
and column
values we save into the database as aliases to the real things, so if the real thing changes, we continue to save with the same model
or column
values, but update what the alias points to and then our old data remains valid.
YAML file
We would define a YAML file:
---
<model>:
model: <actual model>
columns:
<column>: <actual column>
Example of a definition within the YAML:
---
MyModel:
model: MyModel
columns:
user_id: user_id
Over time, if the model name changes, or column names change, the keys would remain unchanged, but the values would be updated:
---
MyModel:
model: Imports::MyModel
columns:
user_id: author_id
Use the aliases in the model
We will update the model added in !156241 (merged) to refer to the aliases when looking up:
-
model
example: (aliases.dig(model, :model)
) -
column
example (aliases.dig(model, :columns, column
) - column names in
composite_key
(using the same method as withcolumn
)
model
, or combinations of <model>
and <column>
Tolerant of unknown We should not block the ability for something we don't know about yet to JustWork and still be saved and used successfully without us officially knowing about it.
!156672 (merged) will need to use the aliased values instead of the real values here. If that MR has not merged by the time this issue would otherwise be completed, we could create a follow-up issue to adjust it to use the aliased values.
When those lines read an unrecognised <model>
or combination of <model>
and <column>
, in prod we should return the value and track a helpful exception (not raise one), and in dev and test we would raise the helpful exception.
If it gets to prod, we would eventually spot the exceptions we tracked (they don't contribute to our error budget) and could add it to the YAML later.
We could do this, for example like:
# within Import::SourceUserPlaceholderReference
def aliased_model
aliases.dig(model, :model) || <track exception and return model>
end
and then update the lines to use aliased_x
methods instead of accessing the properties directly.
Or alternatively, maybe make #model
return the aliased model value and #raw_model
return the database value.
Validation through a spec
We will also add a kind of static validation within a spec scenario. We will fail when the model name we expect no longer exists, or when a column name we expect doesn't exist for the model. The spec failure would alert a developer to update what our aliases map to. This would be similar to one to detect changes relevant to DesignManagement::CopyDesignCollection::CopyService
which has successfully alerted people as what to do as the blame shows. We have a similar concept of keeping import_export.yml
up to date).
We would need to ensure this spec runs after any change to app/models
and ee/app/models
and db/migrate
in our partial merged pipeline results logic, which likely requires updating tests.yml
(see !153092 (merged)).