Skip to content

Implement transactions API#733

Draft
anovakovic01 wants to merge 5 commits intozio:mainfrom
anovakovic01:feature/transactions
Draft

Implement transactions API#733
anovakovic01 wants to merge 5 commits intozio:mainfrom
anovakovic01:feature/transactions

Conversation

@anovakovic01
Copy link
Copy Markdown
Member

This PR resolves #156.

Summary:

  • Add support for MULTI-EXEC transactions,
  • Add compositional zipping of commands in a Redis transaction,
  • Add a transactional version of all commands,
  • Add a dedicated transactional connection pool.

Comment on lines +888 to +903
val lcount = left match {
case value: TransactionOutput[_, _, _] => value.count
case _ => 1
}

val rcount = right match {
case value: TransactionOutput[_, _, _] => value.count
case _ => 1
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You can factor these two out, not much benefit from nesting them this way.

val left: Output[A]
val right: Output[B]

def tryDecode(respValue: RespValue)(implicit codec: BinaryCodec): Out =
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should probably be final.

Comment on lines +840 to +841
val left: Output[A]
val right: Output[B]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

These can be defs.

def tryDecode(respValue: RespValue)(implicit codec: BinaryCodec): Out =
respValue match {
case RespValue.Array(values) =>
this match {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's introduce self (both for consistency and safety).

this match {
case _: Zip[_, _] =>
(left, right) match {
case (l: TransactionOutput[_, _, _], r: TransactionOutput[_, _, _]) =>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Don't introduce names, shadow them instead - less concepts to deal with.

Comment on lines +902 to +915
final case class Zip[+A, +B](
left: Output[A],
right: Output[B]
) extends TransactionOutput[A, B, (A, B)]

final case class ZipLeft[+A, +B](
left: Output[A],
right: Output[B]
) extends TransactionOutput[A, B, A]

final case class ZipRight[+A, +B](
left: Output[A],
right: Output[B]
) extends TransactionOutput[A, B, B]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: I think you can write them down in a line-per-class manner:

final case class Zip[+A, +B](left: Output[A], right: Output[B]) extends TransactionOutput[A, B, (A, B)]

Comment on lines +22 to +23
def codec: BinaryCodec
def executor: RedisExecutor
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why do you need them to be public?


def commit: ZIO[Redis, RedisError, Out] =
ZIO.serviceWithZIO[Redis] { redis =>
this match {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

self (for reasons mentioned above)

import zio.schema.codec.BinaryCodec
import zio.{URLayer, ZLayer}

trait Redis extends api.Sets
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This seems to duplicate the entire API, do we really need it? Can't we have transactional combinators on top of the existing API and use transactions as "scoping" mechanism?

(
l.tryDecode(RespValue.Array(values.take(l.count))),
r.tryDecode(RespValue.Array(values.drop(l.count).take(r.count)))
).asInstanceOf[Out]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We have some "gray zone" safety here.

Add transactional package. Add transactional sets API. Add few
transactional sets tests.

Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com>
Extract all of the commands to the commands package in order to
share them between transactional and normal API.

Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com>
Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com>
@anovakovic01 anovakovic01 force-pushed the feature/transactions branch from e0a8ed5 to c0fd6d1 Compare March 20, 2023 18:00
@anovakovic01 anovakovic01 requested a review from jdegoes March 20, 2023 18:00
Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com>
Signed-off-by: Aleksandar Novakovic <anovakovic01@gmail.com>
}
),
suite("Stralgo")(
suite("StrAlgo")(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's do this in a separate PR.

Comment on lines +43 to +47
def zipLeft[A](that: RedisTransaction[A]): RedisTransaction[Out] =
ZipLeft(this, that)

def zipRight[A](that: RedisTransaction[A]): RedisTransaction[A] =
ZipRight(this, that)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Use self

sealed trait RedisTransaction[+Out] { self =>
import RedisTransaction._

def commit: ZIO[Redis, RedisError, Out] =
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

All methods should be final

private[redis] trait Cluster extends RedisEnvironment {
import Cluster._

final val _asking: RedisCommand[Unit, Unit] = askingCommand(codec, executor)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Two things:

  • let's not name things like this
  • are these exist to "help" with transactional package?

Generally speaking, they may be useful, but we don't have a "proof" for that (i.e. benchmarks), and they don't improve anything on schema-dependent commands.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

These are used by both regular API and Transactional one. The idea was to define a command once and use it from both APIs. It was not introduced for optimization purposes. Also, I've done this because I don't know how to use the same API for both. The reason is the runtime behaviour. Regular API sends a command and receives a response, while the transactional one sends a command and has to remember what type of output it would have to parse later when EXEC is sent. That's why I had to introduce RedisTransaction which is basically transactional command.

Comment on lines +29 to +30
case Single(command, in) =>
command.run(in)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is it possible reuse the existing API here (e.g. pass by name), rather than having a dedicated package full of duplicates?

import zio.redis.{Output, RedisCommand, RedisError}
import zio.{ZIO, Zippable}

sealed trait RedisTransaction[+Out] { self =>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd still prefer it in zio.redis rather than in a separate package.

@mijicd
Copy link
Copy Markdown
Member

mijicd commented Apr 15, 2023

Offtopic: please sync with master or re-open as it's diverging quickly :)

@guizmaii guizmaii changed the base branch from master to main February 12, 2025 07:36
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.

Implement transactions API

2 participants