Brendonovich / prisma-client-rust

Type-safe database access for Rust
https://prisma.brendonovich.dev
Apache License 2.0
1.84k stars 108 forks source link

Creating associated data transactionally #236

Closed lostfictions closed 1 year ago

lostfictions commented 1 year ago

Hi again -- I'm following up from #234 (since this seems like a distinct issue and since I know some maintainers don't check closed issues as a matter of course).

I'm trying to create some associated data transactionally with _batch(), but it doesn't seem possible to create heterogeneous, dynamically-sized data at the moment.

For example, I'd like to create a Post alongside a variable number of Tags based on some input data; as discussed in #234, I'm upserting the tags instead of using connectOrCreate. But if the creating the Post fails for whatever reason, I'd like the data to remain in a consistent state -- any previously-created Tags should be rolled back.

It seems like I can batch a fixed number of heterogeneous operations by passing a tuple to _batch(), or I can batch a variable number of operations of the same type by passing an iterable. But if I don't know the number of Tags to be created, I can't create them and the Post in a single transaction. Is that correct?

As far as I can tell, there's no escape valve for this at the moment either, right? I don't believe I can execute a series of raw statements that start with BEGIN and end with COMMIT, say.

Brendonovich commented 1 year ago

The situation you describe could be implemented in multiple ways, none of which are actually possible yet:

Batching

I've been thinking about how batching multiple types of query containers at the same time could work, since Prisma Client JS allows you to batch whatever you want. In your example, something like _batch((tag::Create, Vec<tag::Upsert>)) would do the trick, but at the moment you can only use either a tuple or a Vec as a batch container.

Allowing this would require some extra work on my part, since I'd have to keep track of how many queries were provided to each tuple and Vec, receive the resulting data as one big Vec, and then partition the results back out to tuples and Vec of the appropriate size.

I may try implement this for 0.6.4 since it makes the ergonomics of batching much nicer.

Nested Writes (#44)

Nested writes would be performed in a transaction, that's just how Prisma works. Not much more to say there.

Interactive Transactions (#60)

On the interactive-transactions branch I currently have the _transaction() client function working, where you can pass in a closure returning an async block that is free to execute what it wants, and if Err is returned at any point then the transaction is rolled back. It's not as efficient as the first two methods but it's the closest to being a reality right now.

Brendonovich commented 1 year ago

Gonna ship the nested batching (#237) in 0.6.4, so that'll be your best option once that's released.

lostfictions commented 1 year ago

Thanks for the quick work! Exciting to hear that interactive transactions are on the way too -- they solve a whole category of problems that are hard to work around otherwise, even if they're a bit of a blunt instrument in cases where nested writes or batching would suffice.