Skip to content

Centralize and standardize the database layer on SQLAlchemy (#3114) - #3308

Open
dbeltrankyl wants to merge 10 commits into
masterfrom
3114-single-db-layer
Open

dbeltrankyl wants to merge 10 commits into
masterfrom
3114-single-db-layer

Conversation

@dbeltrankyl

Copy link
Copy Markdown
Collaborator

Fixes #3114

This is the big cleanup: all database access now lives under
autosubmit/database/ and goes through SQLAlchemy Core. No more raw sqlite3,
no more separate SQLite-only managers; the same code works on SQLite and
PostgreSQL.

It was a lot, so it's split into a 7 logical steps: ( The reviewer can compare commit by commit or just check all at once as you wish)

  • Portable schema tracking first. Instead of relying on SQLite's
    pragma user_version, each target database now tracks its own schema version in a schema_migrations table.
  • The history manager was aligned. Lookups and bulk updates were made consistent, and the temporary table in select_jobs_data is gone (we chunk the IN clause instead).
  • One history manager, not two. The legacy SQLite-only managers are deleted.
  • Engine and session handling was standardized. We use URL.create, define a
    clear pooling policy (including an in-memory StaticPool for tests), and
    record schema versions with a dialect upsert so concurrent writers don't race.
  • The general database and experiment details are now SQLAlchemy-only. That
    let us drop the last raw-sqlite3 paths, the autosubmit.sql schema file and
    the old db_version table.
  • Everything moved into autosubmit/database/, split into models/ and
    managers/.

Finally some QoL extracted a shared SchemaVersionedManager, cleaned up docstrings and type hints, and removed helpers that had no real callers anymore.

One terminology note: here "schema version" means the per-target schema_migrations tracking we added.

The SQLite to PostgreSQL data migration is a different thing and is still pending (see below).

Not in this one

Check List

  • I have read CONTRIBUTING.md.
  • Contains logically grouped changes (else tidy your branch by rebase).
  • [] Does not contain off-topic changes (use other PRs for other changes).
  • Applied any dependency changes to pyproject.toml.
  • Tests are included (or explain why tests are not needed).
  • Changelog entry included in CHANGELOG.md if this is a change that can affect users.
  • Documentation updated.
  • If this is a bug fix, PR should include a link to the issue (e.g. Closes #1234).

@dbeltrankyl dbeltrankyl added this to the 4.2.1 milestone Sep 28, 2026
@dbeltrankyl dbeltrankyl self-assigned this Sep 28, 2026
@codecov

codecov Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.42366% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.87%. Comparing base (a0640f3) to head (f7f592f).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
autosubmit/database/managers/history.py 96.88% 3 Missing and 5 partials ⚠️
autosubmit/database/db_common.py 93.40% 2 Missing and 4 partials ⚠️
autosubmit/scripts/dbmigrate.py 71.42% 4 Missing ⚠️
autosubmit/history/experiment_history.py 93.18% 3 Missing ⚠️
autosubmit/database/managers/base.py 93.75% 1 Missing and 1 partial ⚠️
autosubmit/install.py 0.00% 0 Missing and 2 partials ⚠️
autosubmit/history/strategies.py 96.87% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3308      +/-   ##
==========================================
- Coverage   85.28%   82.87%   -2.42%     
==========================================
  Files         148      152       +4     
  Lines       22173    21555     -618     
  Branches     3934     3878      -56     
==========================================
- Hits        18911    17863    -1048     
- Misses       2501     2911     +410     
- Partials      761      781      +20     
Flag Coverage Δ
fast-tests 82.87% <96.42%> (-2.42%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
autosubmit/config/basicconfig.py 90.50% <100.00%> (+0.12%) ⬆️
autosubmit/database/db_utils.py 100.00% <100.00%> (ø)
autosubmit/database/managers/details.py 100.00% <100.00%> (ø)
autosubmit/database/managers/history_edges.py 93.75% <100.00%> (ø)
autosubmit/database/managers/job_list.py 93.04% <100.00%> (ø)
autosubmit/database/managers/metrics.py 100.00% <100.00%> (ø)
autosubmit/database/managers/status.py 100.00% <100.00%> (ø)
autosubmit/database/models/records.py 100.00% <ø> (ø)
autosubmit/database/models/tables.py 100.00% <100.00%> (ø)
autosubmit/database/schema_version.py 100.00% <100.00%> (ø)
... and 18 more

... and 17 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@dbeltrankyl

Copy link
Copy Markdown
Collaborator Author

Coverage could be improved... on it

@kinow kinow left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@dbeltrankyl managed to review 52 out of 76 files. Lots of changes, but quite easy as there are many that are just imports, fixing formatting, and many tests -- thanks for these improvements!

I left a few comments as I was reading it during the CES meeting 😶‍🌫️ 😅

Also, I think it needs to be rebased now. The docs may have a conflict soon after Suraj PR is merged.

Thanks!

Comment thread CHANGELOG.md
- Fix `RERUN` failing with `TypeError` on `get_job_related()` during `create` #3295 (thanks @elliot1377)
- Removed a duplicate Subversion checkout #3310 (thanks @ShivanshShukla)


Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit-pick, but maybe we don't need the extra blank line? 😬

:param in_map: Whether to place the wrapper in ``job_package_map``.
:type in_map: bool
:param expected: Expected return value.
:type expected: bool

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🙇 thank you!



@pytest.mark.parametrize(
"dialect, expected",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I really don't know why, but normally pytest parameters are not separated by spaces -- https://docs.pytest.org/en/7.1.x/example/parametrize.html

Image

with pytest.raises(AutosubmitCritical):
db_common.check_experiment_exists(name, error_on_inexistence=error_on_inexistence)
else:
assert db_common.check_experiment_exists(name, error_on_inexistence=error_on_inexistence) is True No newline at end of file

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Missing EOL

pytest.param({'split': 5, 'date': '1990-01-01', 'chunk': 2}, {"splits": "{'19900101': [2, 4]}"}, True, id="dict_splits_chunk2_exceeded"),
pytest.param({'split': -1, 'date': '1990-01-01', 'chunk': 1}, {"splits": "{'19900101': [2]}"}, True, id="dict_splits_negative_split"),
pytest.param({'split': 2, 'date': '1990-01-01', 'chunk': 1}, {"splits": "{'19900101': [2]}"}, False, id="dict_splits_exact_boundary"),
]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This works too, but most other tests have the cases within the @pytest.parametrized? Maybe better to follow the existing pattern for consistency.


def get_log_file_path(self):
def get_log_file_path(self) -> str:
return os.path.join(self.historiclog_dir_path,f"{self.expid}_log.txt") No newline at end of file

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe add the EOL to this file too (GH UI is showing this as missing).

"""Create SQLAlchemy Core engine and resolves the connection pool class based on the backend.
# Recycle PostgreSQL connections older than this many seconds, so a connection
# silently dropped by the infrastructure is not reused.
_POSTGRES_POOL_RECYCLE_SECONDS = 1800

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

👍 we probably need an issue to make this configurable. Doesn't need to be implemented in this PR IMo.


if db_backend == "sqlite":
if db_path == ":memory:":
if str(db_path) == ":memory:":

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I will read this file later in the IDE, but I thought this code was going to be replaced by something like _is_in_memory. But that function is only for URL.... but it's called after this, in _resolve_engine, I believe... but hard to follow that here in the GitHub UI.

Comment thread autosubmit/database/data/autosubmit.sql Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hooray!

with target.begin() as conn:
conn.execute(CreateTable(table, if_not_exists=True))
else:
target.execute(CreateTable(table, if_not_exists=True))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ah, maybe that's why you added the dbmigrate already? You replaced the old mechanism (which I think was never used, that db_version) by something that works, and that will be used by the command to migrate data? (I read your changes from the bottom-up, that's why I'm seeing this last).

@dbeltrankyl
dbeltrankyl force-pushed the 3114-single-db-layer branch from bc67304 to f7f592f Compare October 8, 2026 13:07
@dbeltrankyl

Copy link
Copy Markdown
Collaborator Author

rebased

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Centralize and standardize DB layer

2 participants