Skip to content

Conversation

@taddes
Copy link
Collaborator

@taddes taddes commented Oct 30, 2025

Description

We need to create the Postgres specific Db struct and associated methods. The majority of API methods will be implemented upon this struct, so this is to initialize the main logic so that the traits can be implemented.

This also encompasses additions to the Postgres-specific database pool implementation.

Several of the trait bounds (methods) have to be satisfied, but this stubs them out so that the PgDb has a common interface that is the same as those for MySQL and Spanner.

Doc comments added in shared error.rs crate for clarity, since it is a shared interface for all databases.

Removed dead code annotations for clippy that are no longer needed in Tokenserver.

Closes STOR-399.

@taddes taddes self-assigned this Oct 30, 2025
@taddes taddes changed the title [WIP] feat: postgres db pool and session implementation feat: postgres db pool and session implementation Oct 31, 2025
@taddes taddes force-pushed the feat/create-postgres-db-struct-impl-pool-STOR-399 branch from f6dba55 to ebf20bb Compare October 31, 2025 22:18
@taddes taddes marked this pull request as ready for review November 3, 2025 19:41
@taddes taddes requested review from chenba and pjenvey November 3, 2025 19:41
@taddes taddes force-pushed the feat/create-postgres-db-struct-impl-pool-STOR-399 branch from 59c38a1 to dc6f9d9 Compare November 3, 2025 21:17

conn2.run_pending_migrations(MIGRATIONS)?;
Ok(())
*/
Copy link
Collaborator

Choose a reason for hiding this comment

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

Multiple tests are simultaneously trying to run the migrations, causing the tests to fail.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I wouldn't expect most if any tests to work yet until more of the impl is stubbed out. What did you try running? May be that the mutability issue experienced in MySQL is not the case here and I can remove the conn2 = conn here. Let me push that as a change and see if it's different for you.

Copy link
Collaborator

Choose a reason for hiding this comment

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

What did you try running?

cargo test --no-default-features --features=syncstorage-db/postgres --features=tokenserver-db/postgres --features=py_verifier

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, it's still too soon to trust those outputs.

Copy link
Collaborator

Choose a reason for hiding this comment

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

To be clear, I'm not suggesting the tests are failing due to any impl. I'm saying the tests error out on the migrations step, which is enabled in this patch.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I can paste in the error message (and stack trace) if you are not getting the same result in your env.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sure, or just slack it. I don't get that, though I have the expected could not init db_pool in get_test_state: DbError { kind: Diesel(SqlError { kind: DieselConnection(CouldntSetupConfiguration(DatabaseError(UnableToSendCommand, "invalid connection string: unexpected EOF"))), for example, which is expected since the db config isn't fully set up.

Copy link
Collaborator

Choose a reason for hiding this comment

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

which is expected since the db config isn't fully set up.

It's the same database url as SYNC_TOKENSERVER__DATABASE_URL though. I did not do any additional setup. I just set the same value for SYNC_SYNCSTORAGE__DATABASE_URL.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I learned from @pjenvey that we use RUST_TEST_THREADS=1 for our unit tests (see the test and test_with_coverage make targets). That fixes this error.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ach, yes, the RUST_TEST_THREADS=1. I have frequently forgotten about those when not using the make targets. Glad that resolves it.

}
}

#[async_trait(?Send)]
Copy link
Collaborator

Choose a reason for hiding this comment

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

@pjenvey if it can be summarized in a couple sentences, why ?Send?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Let @pjenvey give you his full answer too, but simply put, ?Send negates the need to satisfy the trait bound of Send, with ? being the negation (why not !, I'll never know). Send allows for the sharing of references across threads.

Rust doesn't natively allow for the implementation of async functions as traits, which is why importing async_trait and using the macro in trait and their implementation blocks is a way to get around that. Perfect example is all our Db traits. They all implement the same Db trait signature, so this is a way to "make" them async.

#[async_trait(?Send)] makes it such that you can avoid needing to make the Send non-threadsafe portion async. While not the clearest read, there's some info in the Rust async_trait crate

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks for the write-up. I'm interested in why we don't or can't implement Send in this specific context.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sure! We don't need or want to implement Send, since it's just a default from async_trait. In this case since we're dealing with a scenario where we'd get errors since the future can't be sent between threads safely, since our database transactions are not thread-safe and have to be executed on a single thread. Since we can't guarantee that, we want to remove the Send bound. IIRC, when we start any db transaction, the object holds a mutable reference back to the original connection.

Copy link
Member

Choose a reason for hiding this comment

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

@chenba I almost forgot -- I had written up something about this very question on a recent PR here (albeit more than a few sentences): #1799 (comment)

Comment on lines +128 to +150
impl Db for PgDb {
async fn commit(&mut self) -> Result<(), Self::Error> {
PgDb::commit(self).await
}

async fn rollback(&mut self) -> Result<(), Self::Error> {
PgDb::rollback(self).await
}

async fn begin(&mut self, for_write: bool) -> Result<(), Self::Error> {
PgDb::begin(self, for_write).await
}

async fn check(&mut self) -> Result<results::Check, Self::Error> {
PgDb::check(self).await
}

async fn lock_for_read(
&mut self,
params: params::LockCollection,
) -> Result<results::LockCollection, Self::Error> {
PgDb::lock_for_read(self, params).await
}
Copy link
Member

Choose a reason for hiding this comment

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

Like what the spanner impl has, let's move this impl Db into a db/db_impl.rs and impl BatchDb into db/batch_impl.rs.

Also we don't need the older style, extra level of redirection here either: the actual impl of these methods should reside in these trait method definitions themselves, not in e.g. PgDb::commit. So the commit/rollback/begin/check/timestamp methods above in PgDb should move into here, and every other stub'd method should be a todo!() for now instead.

Suggested change
impl Db for PgDb {
async fn commit(&mut self) -> Result<(), Self::Error> {
PgDb::commit(self).await
}
async fn rollback(&mut self) -> Result<(), Self::Error> {
PgDb::rollback(self).await
}
async fn begin(&mut self, for_write: bool) -> Result<(), Self::Error> {
PgDb::begin(self, for_write).await
}
async fn check(&mut self) -> Result<results::Check, Self::Error> {
PgDb::check(self).await
}
async fn lock_for_read(
&mut self,
params: params::LockCollection,
) -> Result<results::LockCollection, Self::Error> {
PgDb::lock_for_read(self, params).await
}
impl Db for PgDb {
async fn commit(&mut self) -> Result<(), Self::Error> {
<impl from above>
}
async fn rollback(&mut self) -> Result<(), Self::Error> {
<impl from above>
}
async fn begin(&mut self, for_write: bool) -> Result<(), Self::Error> {
<impl from above>
}
async fn check(&mut self) -> Result<results::Check, Self::Error> {
<impl from above>
}
async fn lock_for_read(
&mut self,
params: params::LockCollection,
) -> Result<results::LockCollection, Self::Error> {
todo!()
}

Copy link
Member

Choose a reason for hiding this comment

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

FYI I've moved syncstorage-mysql into this style here: #1882

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Should we merge this in first @pjenvey and fold into your changes in #1882 or other way around?

Copy link
Member

@pjenvey pjenvey Nov 5, 2025

Choose a reason for hiding this comment

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

Sure -- either way should work, I don't think these PRs touch any of the same files (I didn't touch syncstorage-postgres)

@taddes taddes force-pushed the feat/create-postgres-db-struct-impl-pool-STOR-399 branch 2 times, most recently from 7b36882 to 000da2a Compare November 6, 2025 19:12
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.

4 participants