ioredis
ioredis copied to clipboard
How does ioredis handle separate commands during a MULTI
I'd like to run a transaction as such:
-
MULTI
-
<some command>
- Run some code based on results, and if no errors are raised:
-
<some command>
- ...
-
EXEC
Essentially I want to the results of the commands mid-transaction.
It seemed like I could disable pipelining for this:
However, my concern now is... since I'm re-using the same ioredis instance across requests, what would happen if another request came in and triggered some redis commands, at the time that MULTI
block hadn't been EXEC
ed yet?
Would the commands caused by the other request go inside the MULTI
block?
Looks like I can't get results mid-transaction even with pipeline: false
?
EDIT: I wasn't aware of this feature of multi({ pipeline: false })
. Looking into it.
However, my concern now is... since I'm re-using the same ioredis instance across requests, what would happen if another request came in and triggered some redis commands, at the time that MULTI block hadn't been EXECed yet?
Yes, it'd go in the multi block. But that'd only happen if you did something asynchronous between the multi and await call (so avoid doing that), e.g. await
, async operations taking callbacks such as setTimeout, etc.
- If the code was all in one block (with no awaits) then nothing could interrupt synchronous code
@TysonAndre so conclusively the current implementation is very risky and error-prone? Avoiding a await
is just non-practical/easy to miss
so conclusively the current implementation is very risky and error-prone? Avoiding a await is just non-practical/easy to miss
No, I'd strongly recommend the first way in the readme instead, which is safe (Creating a Pipeline, adding commands to the pipeline, and calling exec() on the pipeline.). In that example, all of the commands on the Pipeline instance return this
(the pipeline instance), so you can also call
const multiPipeline = redis.multi();
// other code which may have awaits and be interrupted by other uses of redis
multiPipeline.set('foo', 'bar');
// other code which may have awaits and be interrupted by other uses of redis
multiPipeline.get('foo');
const [[errSet, resultSet], [errGet, resultGet]] = await multiPipeline.exec();
https://github.com/luin/ioredis#transaction
Most of the time, the transaction commands
multi
&exec
are used together with pipeline. Therefore, whenmulti
is called, aPipeline
instance is created automatically by default, so you can usemulti
just likepipeline
:redis .multi() .set("foo", "bar") .get("foo") .exec((err, results) => { // results === [[null, 'OK'], [null, 'bar']] });
Essentially I want to the results of the commands mid-transaction.
You can't by design in redis. Redis is usually very fast except for blocking commands.
See https://redis.io/docs/manual/transactions/
Redis Transactions make two important guarantees:
All the commands in a transaction are serialized and executed sequentially. A request sent by another client will never be served in the middle of the execution of a Redis Transaction. This guarantees that the commands are executed as a single isolated operation.
The EXEC command triggers the execution of all the commands in the transaction, so if a client loses the connection to the server in the context of a transaction before calling the EXEC command none of the operations are performed, instead if the EXEC command is called, all the operations are performed.
What are you using transactions for? If you're looking for higher throughput instead, consider enabling https://github.com/luin/ioredis#autopipelining instead
You can't by design in redis.
Oh, okay then. I was trying to essentially replicate how transactions in relational databases work (in the sense of being able to read data right within a transaction).