Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

transaction support #230

Open
DeoLeung opened this issue Feb 8, 2023 · 1 comment
Open

transaction support #230

DeoLeung opened this issue Feb 8, 2023 · 1 comment

Comments

@DeoLeung
Copy link

DeoLeung commented Feb 8, 2023

Hi,

I see transaction support in TODO for a while, is there any plan for that?

thanks :)

@nicolasff
Copy link
Owner

Hi @DeoLeung,

I originally left out transaction support in Webdis because I wasn't sure exactly how to implement it, specifically what the best API would be. Most Webdis requests likely use a single command, doing things like /GET/foo for example.

"Transactions" in Redis use the MULTI/EXEC keywords which simply wrap a list of commands that are all executed at once on Redis' single processing thread. This doesn't really work with the Webdis request structure: I don't think it would make much sense to send things like /MULTI/SET/foo/bar/.../EXEC. Among other issues, commands with a variable number of parameters just couldn't be expressed this way.

Another approach I had considered was to send them as a JSON list of commands in a POST request, e.g. sending this as the request body:

[
  ["MULTI"],
  ["SET", "foo", "1"],
  ["SET", "bar", "2"],
  ["EXEC"]
]

The downside being that you'd have to structure the commands in a special format, which is not something that Webdis does anywhere else; the only "structured data" used in Webdis request is in its responses, where a request to /SET/hello/world returns something like {"SET":[true,"OK"]}.

There is an alternative though, and I know from user reports and questions that people do run MULTI/EXEC transactions this way: you can store your transaction as a Lua script and then ask Redis (via Webdis) to run it. In fact this seems to be the recommended approach from the Redis project, as their documentation suggests that users execute scripts rather that building MULTI/EXEC transactions:

Something else to consider for transaction like operations in redis are redis scripts which are transactional. Everything you can do with a Redis Transaction, you can also do with a script, and usually the script will be both simpler and faster.

Here's an example where Webdis is used to execute the same "transaction" as above that's mutating both foo and bar atomically.

The Lua script can simply be:

redis.call('SET', 'foo', '1');
redis.call('SET', 'bar', '2');

So we can remove spaces and put it all on one line, then encode the quotes and add it after /SCRIPT/LOAD/. Let's preview what that would look like:

$ echo -n 'http://127.0.0.1:7379/SCRIPT/LOAD/'"redis.call('SET','foo','1');redis.call('SET','bar','2');" | sed -e "s/'/%27/g"
http://127.0.0.1:7379/SCRIPT/LOAD/redis.call(%27SET%27,%27foo%27,%271%27);redis.call(%27SET%27,%27bar%27,%272%27);

If you're building this command from a programming language, you just need to URL-encode the script, e.g.

> encodeURIComponent("redis.call('SET','foo','1');redis.call('SET','bar','2');")
"redis.call('SET'%2C'foo'%2C'1')%3Bredis.call('SET'%2C'bar'%2C'2')%3B"

Let's run our command by piping this encoded URL to xargs curl:

$ echo -n 'http://127.0.0.1:7379/SCRIPT/LOAD/'"redis.call('SET','foo','1');redis.call('SET','bar','2');" | sed -e "s/'/%27/g" | xargs curl
{"SCRIPT":"61202b13748238bf45d58fd21c8ad16831f7a4c1"}

This is our script hash; before running it let's first make sure foo and bar don't exist:

$ curl http://127.0.0.1:7379/DEL/foo/bar
{"DEL":0}

Then invoke the script with 0 parameters:

$ curl http://127.0.0.1:7379/EVALSHA/61202b13748238bf45d58fd21c8ad16831f7a4c1/0
{"EVALSHA":null}

And read back foo and bar:

$ curl http://127.0.0.1:7379/MGET/foo/bar
{"MGET":["1","2"]}

I hope the example helps! This should cover all use cases where MULTI/EXEC was previously required, and seems to be the way forward for multi-command operations as recommended by the Redis project. Thanks for bringing my attention to this note in the Webdis docs, I'll update them to mention Lua scripts instead.

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

No branches or pull requests

2 participants