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

Add Lua Scripting support to nri-redis #109

Merged
merged 37 commits into from
May 30, 2024

Conversation

omnibs
Copy link
Member

@omnibs omnibs commented May 21, 2024

Add an API to do Lua scripting to Nri.Redis.

We have a new Handler function eval, which does the recommended dance:

  • Calculate the SHA1 of the Lua script we intend to run
  • Try to run it with EVALSHA ${sha1}
    • On failure:
      • LOAD SCRIPT ${theScript}
      • Retry EVALSHA ${sha1}

The failure case only happens:

  • The very first time a Lua script is tried on a specific Redis server
  • The very first time any changes to the Lua script are tried on a specific Redis server

I initially tried to do this as a Redis.Api / Internal.Query, but it didn't work. If I understand semantics of type classes correctly, it won't unless Internal.Query is a Monad, not an Applicative.

Hedis uses a different RedisResponse typeclass for scripting results, which has its own decoding thingy. I wrote a few orphaned instances for common data types, like Text, Int and ().

Notes:

  • The eval API supports namespacing of keys
  • The eval API does not support auto-extending key expiry

omnibs added 3 commits May 20, 2024 18:46
The core will be in Redis.Script
Turns out error messages are important
Copy link

linear bot commented May 21, 2024

omnibs added 9 commits May 21, 2024 00:29
This is failing bc I misunderstood `eval` in Redis

Eval is: EVAL script numkeys key key key arg arg arg

Keys here is not the NAME of the argument, as I thought, but rather an
input parameter in full

There's no named arguments in Redis. I assumed that by reading examples
from the C# API Client in the Rate Limiting implementation docs.

Args and Keys are referenced like KEYS[1] KEYS[2] KEYS [3] and ARGV[1]
ARGV[2] ARGV[3]

Let's fix that in the next few commits
@omnibs omnibs force-pushed the phx-1356-add-eval-api-to-nri-redis branch from e7e63a7 to 4e3fb2a Compare May 22, 2024 21:01
omnibs added 15 commits May 23, 2024 17:15
Splits up keys and arguments, uses KEYS[1] and ARGV[1] inside luaScript
This required a RedisResult instance for Text
Redis provides a faster API for running scripts, where you:
- SCRIPT LOAD "the script"
- EVALSHA "script sha1" n_keys [keys] [args]

I can't implement this because we'd need to restrict `doRawQuery`.

`doRawQuery` is generic for both instances of `RedisCtx` in Hedis:
- Transactions: `RedisCtx RedisTx Queued`
- Regular queries: `RedisCtx Redis (Either Reply)`

I'll need a different approach for this.
Allows us to do the `evalsha -> script load -> evalsha` flow
The Handler eval is better. The only thing it doesn't support is
auto-expire, but I'm skeptical it's a good idea to use that with Lua
scripts arbitrarily.
@omnibs omnibs changed the title Phx 1356 add eval api to nri redis Add Lua Scripting support to nri-redis May 27, 2024
@omnibs omnibs marked this pull request as ready for review May 27, 2024 22:33
@omnibs omnibs requested a review from micahhahn May 27, 2024 22:33
Copy link
Member

@micahhahn micahhahn left a comment

Choose a reason for hiding this comment

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

Amazing work! I really appreciate us going the extra mile here to make a good interface for this.

A couple things to change below, but nothing deal breaking.

Comment on lines 120 to 124
-- We can't test for compile-time errors, but manually test our helpful error message, uncomment
-- the lines below:
-- Test.test "compilation error" <| \_ ->
-- [script|${123}|]
-- |> Expect.equal "Doesn't matter, this won't compile"
Copy link
Member

Choose a reason for hiding this comment

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

Sooo, we can in fact test for compile-time errors :)

The basic idea is that you call your function normally (not in a splice), and then there are tools in the Language.Haskell.TH.TestUtils module which let you run the Q monad and test the result.

You might want to take a look at what I did in nri-postgresql/test/Enum.hs way back when I added postgres enum support.

Copy link
Member

Choose a reason for hiding this comment

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

Also, let's pair if this is something you would want to add!

nri-redis/test/Spec/Redis/Script.hs Outdated Show resolved Hide resolved
nri-redis/src/Redis/Script.hs Outdated Show resolved Hide resolved
@omnibs omnibs requested a review from micahhahn May 29, 2024 22:38
@omnibs omnibs merged commit cb753bc into trunk May 30, 2024
6 checks passed
@omnibs omnibs deleted the phx-1356-add-eval-api-to-nri-redis branch May 30, 2024 16:39
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.

3 participants