use redis::Script to handle script hash caching
authorGeoffrey Allott <geoffrey@allott.email>
Fri, 5 Feb 2021 17:06:31 +0000 (17:06 +0000)
committerGeoffrey Allott <geoffrey@allott.email>
Fri, 5 Feb 2021 17:06:31 +0000 (17:06 +0000)
src/server.rs

index 155c0c07e28b496f699a514f263a494e401cd187..0e32637d8cd21e20482de8089c178b1afbb17c6f 100644 (file)
@@ -2,7 +2,7 @@ use std::collections::HashSet;
 use std::convert::TryFrom;
 
 use futures::{channel::mpsc::{Receiver, Sender, channel}, SinkExt};
-use redis::{AsyncCommands, ErrorKind, FromRedisValue, Msg, RedisError, RedisResult, RedisWrite, ToRedisArgs, Value, aio::MultiplexedConnection};
+use redis::{AsyncCommands, ErrorKind, FromRedisValue, Msg, RedisError, RedisResult, RedisWrite, Script, ToRedisArgs, Value, aio::MultiplexedConnection};
 use serde::{Serialize, Deserialize};
 
 use crate::auth::Auth;
@@ -69,6 +69,19 @@ impl ToRedisArgs for ClientInterest {
     }
 }
 
+const TAKE_ACTION_LUA_SCRIPT: &'static str = r#"
+    local len = redis.call('llen', KEYS[1])
+    local expected = tonumber(ARGV[1])
+    if (len == expected) then
+        return redis.call('rpush', KEYS[1], ARGV[2])
+    elseif (len > expected) then
+        return 0
+    else
+        local error = string.format('Inconsistent game state - database reports %d actions but expected at least %d', len, expected)
+        return redis.error_reply(error)
+    end
+"#;
+
 impl Server {
     pub fn new(redis: MultiplexedConnection, register_update_stream: Sender<ClientInterestSender>) -> Self {
         Self {
@@ -82,13 +95,15 @@ impl Server {
         if let Err(err) = self.register_update_stream.clone().send(client_interest_sender).await {
             error!("new_state: Send failed: {}", err);
         }
-        (ServerState{redis: self.redis.clone(), register_interest}, interest)
+        let take_action_script = Script::new(TAKE_ACTION_LUA_SCRIPT);
+        (ServerState{redis: self.redis.clone(), register_interest, take_action_script}, interest)
     }
 }
 
 pub struct ServerState {
     redis: MultiplexedConnection,
     register_interest: Sender<HashSet<ClientInterest>>,
+    take_action_script: Script,
 }
 
 fn user_key(username: &str) -> String {
@@ -99,19 +114,6 @@ fn game_key(id: u32) -> String {
     format!("game:{}", id)
 }
 
-const TAKE_ACTION_LUA_SCRIPT: &'static str = r#"
-    local len = redis.call('llen', KEYS[1])
-    local expected = tonumber(ARGV[1])
-    if (len == expected) then
-        return redis.call('rpush', KEYS[1], ARGV[2])
-    elseif (len > expected) then
-        return 0
-    else
-        local error = string.format('Inconsistent game state - database reports %d actions but expected at least %d', len, expected)
-        return redis.error_reply(error)
-    end
-"#;
-
 impl ServerState {
     pub async fn create_user(&mut self, username: &str, auth: Auth, nickname: &str) -> RedisResult<()> {
         let key = user_key(username);
@@ -182,8 +184,8 @@ impl ServerState {
 
     pub async fn take_action(&mut self, id: u32, len: usize, action: &UserAction) -> RedisResult<ActionStatus> {
         let key = game_key(id);
-        debug!("redis: executing: EVAL {:?} 1 {:?} {} {:?}", TAKE_ACTION_LUA_SCRIPT, key, len, serde_json::to_string(&action).unwrap());
-        redis::cmd("EVAL").arg(TAKE_ACTION_LUA_SCRIPT).arg(1).arg(key).arg(len).arg(AsJson(action)).query_async(&mut self.redis).await
+        debug!("take_action: EVAL {{TAKE_ACTION_LUA_SCRIPT}} 1 {} {} {:?}", key, len, action);
+        self.take_action_script.key(key).arg(len).arg(AsJson(action)).invoke_async(&mut self.redis).await
     }
 
     pub async fn register_interests(&mut self, interests: HashSet<ClientInterest>) {