1. Matthew Frazier
  2. leafstorm.us


Matthew Frazier  committed ee13cff

added article "Lua Errors, Redis Errors"

  • Participants
  • Parent commits 005a048
  • Branches default

Comments (0)

Files changed (1)

File content/articles/lua-errors-redis-errors.md

View file
+title: Lua Errors, Redis Errors
+slug: lua-errors-redis-errors
+created_at: 2011-07-06 13:31:41 -0400
+kind: article
+tags: [programming, redis, lua, databases]
+summary: "How Redis should translate between them."
+Redis's scripting branch is currently enjoying quite a bit of popularity, and while
+I have not had the chance to play around with it yet, I am certainly glad
+that antirez chose Lua as the language to use. (The fact that he was able
+to deliver a working implementation so quickly is a testament to the power
+and flexibility of Lua.)
+Still, there are a couple of things that bug me about the Redis/Lua type
+conversion. One is the representation of a Redis nil as a Lua false, but
+considering the pain that is Lua nils and tables antirez did not really have
+any other option (except maybe adding an `n` value). The one that really
+bugs me is the representation of errors. Namely:
+    a_redis_error = {err = "Error message here"}
+When I asked antirez why he did not just raise Lua errors when a Redis error
+is encountered, he responded:
+> My point for using {err = ...} is that I want all the errors be single
+> return values for a matter of unification [...]
+> Similarly you should be able to call a Redis command without even caring
+> about what he returned, save the reply into a var, and return it later as
+> the user directly called Redis: [...]
+> I think this is much more important compared to Lua idiomatic error handling
+> since what hides inside the above concept is that Redis errors are a data
+> type and not a condition.
+But I grepped through the Redis source code looking for calls to
+`addErrorReply`, and what I found was that error replies are used in such
+situations as:
+* Operations on the incorrect data type.
+* Strings or integers exceeding the maximum size.
+* Incorrect numbers of arguments.
+* Improper arguments such as negative timeouts, or scores not formatted as
+  floats.
+* Selecting a database that is out of range.
+* Improperly formatted network requests.
+* A key living in a different node on the cluster (i.e. being "out of scope").
+All of those cases are "programming errors," so to speak - they represent
+cases where the operation could not be completed either (a) because the
+programmer did something wrong, or (b) due to unavoidable constraints on the
+database itself. In fact, in the normal Redis client libraries, Redis errors
+are usually handled by raising exceptions.
+Also, Redis and Lua are remarkably consistent in when errors and nils
+are used. If you try to get a key that does not exist from a Lua table or a
+Redis database, they both return `nil`. If you try to do an operation that a
+data type does not support, they both raise an error. If you call a Lua
+core function or a Redis command with the wrong number of arguments, they
+still both raise an error.
+And in most cases, if one of the errors above is generated during a script,
+it means that the rest of the operation is not likely to succeed either. So,
+I still believe that the best way to handle errors in Redis and Lua is:
+* If a Redis command returns an error, raise an error on the Lua side.
+* If the Lua script does not catch the error, or generates an error of its
+  own, return it to the client as a Redis error reply.
+And if you are expecting an error to happen in your script, then you can
+simply use `pcall`:
+    local ok, result = pcall(redis.call, "DANGER", "dangerous", "command")
+    if ok then
+        -- it worked! result is the call's actual result
+    else    
+        -- the Redis command errored! result is the error message
+    end
+If this use case is common enough, it may be worth it to add a `redis.pcall`
+function that basically just does the same thing as pcall'ing `redis.call` -
+return `true, result` if it works, and `false, message` if it doesn't.
+The point is, representing Redis errors as a normal data type might be worth
+it if they were commonly returned for things like missing keys or deleting
+things that weren't there to begin with. But they aren't - they are used for
+truly exceptional conditions. Which is exactly what Lua errors are designed
+to represent.