Skip to content

Support for persistent sessions using local and remote (LaunchIT) MariaDB - #168

Open
amarathe84 wants to merge 5 commits into
FLASK-LLNL:mainfrom
amarathe84:database-v3
Open

Support for persistent sessions using local and remote (LaunchIT) MariaDB#168
amarathe84 wants to merge 5 commits into
FLASK-LLNL:mainfrom
amarathe84:database-v3

Conversation

@amarathe84

Copy link
Copy Markdown
Collaborator

Description

This PR adds database-backed session persistence to the Flask Copilot:

  • Support for persistent sessions local and remote
  • Automatic checkpointing - Saves experiment state to MariaDB each time a new molecule is generated
  • Manual checkpointing - "Checkpoint" dropdown button allows on-demand saves and toggling auto-save on/off
  • Session restore on reconnect - Automatically restores the last session (molecules, graph layout, reasoning messages) when reopening the app
  • Project/experiment tracking - Each checkpoint stores project ID, experiment ID, and full experiment metadata
  • Complete state preservation - Saves tree nodes, edges, sidebar reasoning messages, metrics history, zoom/pan settings, and property optimization config
  • Duplicate prevention - Prevents duplicate molecules when restoring mid-computation sessions
  • Graceful resume handling - Detects interrupted computations and attempts to resume or gracefully resets

Minor code hardening:

  • Moved credentials to environment variables (no hard-coding)
    -Add automatic connection retry in case of network connection failure or stalled session. Also added a timeout for the overall experiment
    -Fix the SSL configuration (no more fake_flag_to_enable_tls used previously)
  • Add cleanup for sync_engine on failure - Currently only async_engine is disposed in main()
  • Unit testing (CLI) for state consistency
  • Code changes for integration with LC user management

Unit/integration testing:

Checkpoint Creation & Updates

  1. Test checkpoint creation on node arrival
  2. Test checkpoint updates incrementally as nodes arrive
  3. Test checkpoint created when no prior state exists
  4. Test checkpoint merges with existing partial state

Checkpoint Persistence


5. Test checkpoint persists correct node count after disconnect


6. Test checkpoint persists correct edge count after disconnect


7. Test serverSessionId is correctly stored in checkpoint


8. Test isComputing flag is set to false in checkpoint after disconnect


9. Test checkpoint contains all required state fields (smiles, problemType, etc.)


10. Test checkpoint timestamp is updated on each save

Checkpoint Restoration


11. Test checkpoint restoration via /sessions/latest endpoint

12. Test checkpoint restoration via /sessions/{id} endpoint


13. Test resumed computation continues from checkpoint

Some Corner Case Handling


14. Test checkpoint handles malformed node data gracefully


15. Test checkpoint with maximum depth computation (compute complete and restore)

Comment thread db_backend/database/engine.py Outdated
Comment thread db_backend/database/engine.py Outdated
SSL_KW = {}

# Create sync engine (SSL required by server; skip cert verification because tunnel is trusted)
try:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

using the global namespace to run this code is problematic. uvicorn uses fork and so sometimes these objects are not preserved well. The engines need to be created lazily in a function context

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

To address this, engine creation is now lazy and happens with get_async_engine() rather than at module import time. The module-level globals are only placeholders and are not constructed engine objects. Let me know if this looks good

@tbennun tbennun Mar 13, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

engine is still assigned in the global context...

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

please also refactor this lengthy code into a function

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

These were due to incorrect force-push from another branch and conflict resolution which didn't go as planned. Both of these (local cache instead of global context and refactor) have been handled now.

Comment thread db_backend/database/engine.py Outdated
with sync_engine.connect() as _conn:
_conn.execute(text('SELECT 1'))

# Also create async engine for compatibility

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why two engines?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The sync engine functionality was initially added for CLI/debugging at my end with a couple of standalone test scripts to check consistency of database, functionality flow etc.. I’ve removed the sync engine since it isn’t used (the tests scripts are not part of this PR). I have a separate branch where I'm keeping the functionality required for debugging.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I can still see it above. Is there a merge conflict?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, I had messed it up with the last push. I've correctly merged and pushed from my local branch

Comment thread db_backend/database/models.py Outdated
sidebar_state: Mapped[Optional[str]] = mapped_column(JSON, nullable=True)

# Experiment context
experiment_context: Mapped[Optional[str]] = mapped_column(Text, nullable=True)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why is this Text and not JSON?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

FIxed this. experiment_context is now JSON based. Was originally plaintext for readability but I've switched to JSON

Comment thread db_backend/database/schemas.py Outdated
Comment thread db_backend/database/schemas.py Outdated
Comment thread db_backend/routers/projects.py Outdated


# Pydantic models
class ExperimentData(BaseModel):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this repeats what we have in database/models, but looks better? Consolidate the two classes please

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes makes sense. Now these are combined into schemas.py. Code in projects.py  imports and uses the shared schema definitions instead of redefining mapping classes locally.

Comment thread .gitignore Outdated
Comment thread .gitignore Outdated
Comment thread db_backend/database/engine.py Outdated
Comment on lines +94 to +95
with sync_engine.connect() as _conn:
_conn.execute(text("SELECT 1"))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

only one process will execute this!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch, silly mistake (based on my incorrect scope understanding earlier). New update to the code takes out eager validation and startup time DB side effect issue.

- Add db_backend/ package with SQLAlchemy models, schemas, and REST API
  routers for projects and sessions (CRUD + auto-save/restore)
- Add useSessionPersistence hook for frontend auto-checkpoint logic
- Update App.tsx with experiment persistence, restore-on-reload, and
  problemType fallback when DB row is not yet populated
- Update project_sidebar.tsx with save/checkpoint UI controls
- Update useProjectData.ts to integrate with the new DB backend
- Add db_config.py for database connection configuration
- Add .env.example with all required environment variables
- Update mock_server.py with session save/restore, immediate persist
  on compute start, and background checkpoint support
- Update .gitignore with Python, env, and build artifact patterns
- Add sqlalchemy to requirements.txt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants