Skip to content

Conversation

@hjanott
Copy link
Member

@hjanott hjanott commented Sep 23, 2025

Continuation of #2520

@hjanott hjanott added this to the 4.x milestone Sep 23, 2025
@hjanott hjanott self-assigned this Sep 23, 2025
@hjanott hjanott added the migration Introduces a new migration label Dec 11, 2025
hjanott and others added 2 commits December 15, 2025 17:21
Co-authored-by: Alexej <33332102+4echow@users.noreply.github.com>
Copy link
Member

@luisa-beerboom luisa-beerboom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only looked at some of it so far

"SELECT schemaname, tablename from pg_tables where schemaname in ('public', 'vote');"
).fetchall()
return [
f"{row.get('schemaname', '')}.{row.get('tablename', '')}" for row in rows if row not in OLD_TABLES # type: ignore
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why type: ignore?

- You can write migrations in the backend and also adjust the Datastore at the same time since both are mounted into the container.

The following scripts can be used to make snapshots and trying out new migrations
The migrations itself are in the `migrations` folder. Each file must start with a four digit number and can include certain functions that can be found by the loader. A psycopg cursor object will be passed as function parameter.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The migrations itself are in the `migrations` folder. Each file must start with a four digit number and can include certain functions that can be found by the loader. A psycopg cursor object will be passed as function parameter.
The migrations themselves are in the `migrations` folder. Each file must start with a four digit number and can include certain functions that can be found by the loader. A psycopg cursor object will be passed as function parameter.


def setup_migration_relations(self) -> None:
"""Sets the tables and views used within the migration and copies their data."""
# TODO this method needs to be tested.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it even possible to test this before the first post-relDB Migration happens? If it isn't, perhaps write an issue?

Comment on lines +128 to +129
Returns:
- None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this part of the description necessary if you already have the type annotation on the function?

Comment on lines +140 to +143
if getattr(migration_module, "IN_MEMORY", False):
# TODO In-Memory migration
migration_module.in_memory_method()
else:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMM is not planned after relDB update. The TODO etc can be removed.

@staticmethod
def load_migrations() -> None:
"""
Checks wether current migration_index is equal to or above the FIRST_REL_DB_MIGRATION and
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Checks wether current migration_index is equal to or above the FIRST_REL_DB_MIGRATION and
Checks whether current migration_index is equal to or above the FIRST_REL_DB_MIGRATION and

*whether

if tmp := curs.execute("SELECT migration_index FROM version;").fetchall():
return [elem.get("migration_index", 0) for elem in tmp]
raise MigrationException(
"No migration index could not be acquired from database."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double negation in error message :(

).fetchone()
assert (
version_table_exists
), "Something really blew up checking the version tables existence."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol

verbose: bool = False,
print_fn: PrintFunction = print,
) -> None:
"""init"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Description very descriptive. Delete.

Comment on lines +110 to +113
if response:
return response.get("count", 0)
else:
return 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if response:
return response.get("count", 0)
else:
return 0
return (response or {}).get("count", 0)

?

Comment on lines +126 to +128
if mi > current_migration_index
if state_per_mi[mi]
in (MigrationState.MIGRATION_REQUIRED, MigrationState.MIGRATION_RUNNING)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

two if?

Copy link
Member

@luisa-beerboom luisa-beerboom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looked at everything that is there so far

Comment on lines +59 to +60
if result is not None:
if len(result) > 0:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for two lines, connect the conditions with and

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could also just use a questionmark conditional on the return statement to combine all of ln 59 - 62

Comment on lines +150 to +158
# 1.1) OrganizationFields are always generated on DB side
if isinstance(field_class, OrganizationField):
return True

# 1.2) ViewFields are solely for the DB table view
elif field_class.is_view_field:
return True

return False
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# 1.1) OrganizationFields are always generated on DB side
if isinstance(field_class, OrganizationField):
return True
# 1.2) ViewFields are solely for the DB table view
elif field_class.is_view_field:
return True
return False
return isinstance(field_class, OrganizationField) or field_class.is_view_field

Comment on lines +188 to +191
result = cur.execute(
"SELECT theme_id FROM organization_t WHERE id=1;"
).fetchone()
assert result is not None, "1:1 relation in organization not filled."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part and below:
Sometimes you're writing result = cur.execute("...").fetchone() and then assert result = ..., and sometimes you're writing cur.execute("...") and then cur.fetchone().

Two questions:

  1. Why not more consistent?
  2. Why not make a helper function that hides the repeating parts of this pattern for the sake of readability?

Comment on lines +246 to +252
for idx, row in enumerate(curs.fetchall(), 99):
if row["migration_index"] == LAST_NON_REL_MIGRATION:
continue
assert {
"migration_index": idx,
"migration_state": MigrationState.FINALIZATION_REQUIRED,
} == row
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why start the enumeration with 99 and why skip the last non reldb migration index?
What are you checking for here?

Comment on lines +259 to +264
if row["migration_index"] == LAST_NON_REL_MIGRATION:
continue
assert {
"migration_index": idx,
"migration_state": MigrationState.FINALIZED,
} == row
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here why not check it for the last non RelDB migration?

Comment on lines +484 to +487
Input:
n/a
Returns:
n/a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think these 4 lines are necessary. Not for the other test cases either.

Comment on lines +489 to +496
The test cases initially may seem to be spartanic caused by the lack of testing of integrity
after transfering the data from the key-value-store into their respective tables.
What is tested is the correct creation of intermediate tables and simple relations exemplary as we
can trust that it works for other tables if it worked for one.
Also it is tested that the new tables are created on top of an old basis and the old tables are deleted.
It should be only used like this in this migration test since it leads to a performance problem
once the actual connection context is entered the first time after the database was dropped and recreated.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment seems to be under every single test case. Isn't there a more central place this could be put? The check_data function or the top of the file maybe?

Also the text seems to suggest that the integrity of the complete instance is not checked. That breaks with our previous MO as not everything that is changed is also checked. Thinking in terms of what this would mean in the old system: It would risk missing bugs in something as critical as this migration and getting corrupt data as a result. Or missing data. Idk how well that philosophy translates into the new system, but if the risk exists, considering how important this is, I'd suggest testing the content of the new tables more thoroughly in at least one test.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the old system, as far as I recall, the database check was always called automatically after every finalize call in in the test of whatever migration was currently the most recent.

@luisa-beerboom luisa-beerboom removed their assignment Jan 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature migration Introduces a new migration

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants