question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Callback-free interactive transactions

See original GitHub issue

Problem

Some users are asking for a less restricted way for opening an interactive transaction. Instead of having a callback for the transaction, there would be manual calls to $begin, $commit, and $rollback.

Suggested solution

Example of a use-case

describe('some test', () => {
   let trx: Transaction;

   beforeEach(async () => {
      trx = await prisma.$begin();
      jest.mock('./prisma', () => trx);
   });

   afterEach(async () => {
      await trx.$rollback();
   });

   it('.....', () => {
      // ....
   });
});

Alternatives

Additional context

https://github.com/prisma/prisma/issues/1844#issuecomment-1067348856

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:23
  • Comments:14 (5 by maintainers)

github_iconTop GitHub Comments

4reactions
millspcommented, Apr 4, 2022

Hey folks, I prototyped something today (with type safety!). Do you mind giving it a try and share your feedback?

Prototype setup

import { PrismaClient } from "@prisma/client"

type CtorParams<C> = C extends new (...args: infer P) => any ? P[0] : never
type TxClient = Parameters<Parameters<PrismaClient['$transaction']>[0]>[0]
const ROLLBACK = { [Symbol.for('prisma.client.extension.rollback')]: true }

async function $begin(client: PrismaClient) {
    let setTxClient: (txClient: TxClient) => void
    let commit: () => void
    let rollback: () => void

    // a promise for getting the tx inner client
    const txClient = new Promise<TxClient>((res) => {
        setTxClient = (txClient) => res(txClient)
    })

    // a promise for controlling the transaction
    const txPromise = new Promise((_res, _rej) => {
        commit = () => _res(undefined)
        rollback = () => _rej(ROLLBACK)
    })

    // opening a transaction to control externally
    const tx = client.$transaction((txClient) => {
        setTxClient(txClient as TxClient)

        return txPromise.catch((e) => {
            if (e === ROLLBACK) return
            throw e
        })
    })

    return Object.assign(await txClient, {
        $commit: async () => { commit(); await tx },
        $rollback: async () => { rollback(); await tx }
    } as TxClient & { $commit: () => Promise<void>, $rollback: () => Promise<void> })
}

// patches the prisma client with a $begin method
function getTxClient(options?: CtorParams<typeof PrismaClient>) {
    const client = new PrismaClient(options)
    
    return Object.assign(client, {
        $begin: () => $begin(client)
    }) as PrismaClient & { $begin: () => ReturnType<typeof $begin> }
}

Example

const prisma = getTxClient()
await prisma.user.deleteMany()
const tx = await prisma.$begin()

await tx.user.create({
    data: {
        email: Date.now() + '@email.io'
    }
})

const users0 = await tx.user.findMany({})
console.log(users0)

await tx.$rollback()

const users1 = await prisma.user.findMany({})
console.log(users1)
3reactions
millspcommented, Aug 31, 2022

Hey everyone, I am excited to share that we are working on a new proposal that will help in solving this. While we are not keen to add this to our API, we are very willing to allow you to create and share custom extensions. This is how you would do it:

const prisma = new PrismaClient().$extends({
	$client: {
		begin() { ... }, // the code I shared above
	}
})

const tx = await prisma.$begin()

await tx.user.create(...)
await tx.user.update(...)

await tx.$commit()

We would love to see what you can build with Prisma Client Extensions. I’d appreciate if you can take some time to read and share your feedback on the proposal.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Transactions and batch queries (Reference) - Prisma
Interactive transactions : pass a function that can contain user code including Prisma Client queries, non-Prisma code and other control flow to be...
Read more >
Online Detection of Effectively Callback Free Objects with ...
is inspired by conflict serializability of database transactions. The main idea is to explore commuta- tivity of operations for efficient online checking of ......
Read more >
Online Detection of Effectively Callback Free Objects ... - arXiv
Notice that the ECF notion is similar to the notion of atomic transactions in concurrent systems. Indeed, despite the fact that contract ...
Read more >
Online detection of effectively callback ... - ACM Digital Library
Callbacks are essential in many programming environments, but drastically complicate program understanding and reasoning because they allow ...
Read more >
Using Automated Reasoning Techniques for Enhancing the ...
As regards efficiency, due to the huge volume of transactions, the cost and ... Online detection of effectively callback free objects with ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found