Skip to content

Conversation

@tejasbadadare
Copy link
Contributor

@tejasbadadare tejasbadadare commented Jul 9, 2025

Summary

Support both Postgres and SQLite database backends instead of just SQLite. This lets us use a hosted Postgres database that multiple replicas can connect to, instead of a file-based one. We still use an in-memory SQLite for the History tests.

Rationale

A central database shared between the replicas gives us a consistent storage to serve the API from.

Some impl details:

  • Used a generic AnyPool sqlx driver instead of a Pool<Sqlite>. It chooses the correct driver at runtime based on the URL passed in.
  • Unified the migrations into one.
  • Updated DB types and SQL syntax to be cross-compatible with Postgres and SQLite (this means using an integer to store timestamp instead of a Datetime, but afaik there's no way around this due to intricacies in how AnyPool works. Shouldn't be an issue because we handle the type coercion ourselves anyway.)
  • AnyPool doesn't support QueryBuilder properly, so we now manually build the select queries in explorer.rs, similar to the update queries in history.rs.
  • Updated docs

How has this been tested?

  • Current tests cover my changes
  • Added new tests
  • Manually tested the code

Tested Fortuna's /v1/logs (explorer) API with a SQLite backend and a cloud Postgres backend from Neon. Also tested with multiple replicas pointing to the same DB, which is our intended production configuration.

@vercel
Copy link

vercel bot commented Jul 9, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
api-reference ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 9, 2025 9:03pm
component-library ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 9, 2025 9:03pm
developer-hub ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 9, 2025 9:03pm
entropy-debugger ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 9, 2025 9:03pm
entropy-explorer ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 9, 2025 9:03pm
insights ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 9, 2025 9:03pm
proposals ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 9, 2025 9:03pm
staking ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 9, 2025 9:03pm

Copy link
Contributor

@jayantk jayantk left a comment

Choose a reason for hiding this comment

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

LGTM but please address the inline comments

pub async fn new() -> Result<Self> {
Self::new_with_url("sqlite:fortuna.db?mode=rwc").await
let database_url =
std::env::var("DATABASE_URL").unwrap_or_else(|_| DEFAULT_DATABASE_URL.to_string());
Copy link
Contributor

Choose a reason for hiding this comment

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

can this be a config value? it's confusing to have configuration both via a file and via env. I can see this being useful for secrets or something, but this doesn't seem particularly sensitive.

Copy link
Contributor Author

@tejasbadadare tejasbadadare Jul 9, 2025

Choose a reason for hiding this comment

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

The sqlx tool uses dotenv to read the URL from the .env file, so we use that in Fortuna as well (we could load it into the env in a different way if needed in k8s.) It helps devex IMO to use the same .env file in Fortuna that sqlx uses, as sometimes it's necessary to setup/recover migrations using the tool. Also the connection string has credentials in it, so better to consider it a secret rather than a config

Copy link
Collaborator

Choose a reason for hiding this comment

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

i hear you here but it's very confusing to have two ways of passing configs. i think moving this to run.rs might be better and regardless i recommend logging this (as info level) that you are using this data base url to help people catch it if they don't set it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah fair enough, it can be confusing to have 2 config sources. Since we are ok with exposing the DB credentials, I'll go ahead and unify to config.yaml.

.max_lifetime(None)
.connect("sqlite::memory:")
.await?;
let migrator = migrate!("./migrations");
Copy link
Contributor

Choose a reason for hiding this comment

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

is the sqlite_migrations directory used anywhere? if not, delete please

Copy link
Contributor

Choose a reason for hiding this comment

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

(also suggest extracting a string constant for the migrations directory)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yep forgot to delete, thanks

"chrono",
] }
num-traits = "0.2.19"
dotenv = "0.15.0"
Copy link
Collaborator

Choose a reason for hiding this comment

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

this seems abandoned (from sqlx docs), can you use dotenvy?

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.

3 participants