Files

5.7 KiB

Redis Client Reference

Initialization

type MyConfig struct {
    redis wrapper.RedisClient
    qpm   int
}

func parseConfig(json gjson.Result, config *MyConfig) error {
    serviceName := json.Get("serviceName").String()
    servicePort := json.Get("servicePort").Int()
    if servicePort == 0 {
        servicePort = 6379
    }
    
    config.redis = wrapper.NewRedisClusterClient(wrapper.FQDNCluster{
        FQDN: serviceName,
        Port: servicePort,
    })
    
    return config.redis.Init(
        json.Get("username").String(),
        json.Get("password").String(),
        json.Get("timeout").Int(), // milliseconds
        // Optional settings:
        // wrapper.WithDataBase(1),
        // wrapper.WithBufferFlushTimeout(3*time.Millisecond),
        // wrapper.WithMaxBufferSizeBeforeFlush(1024),
        // wrapper.WithDisableBuffer(), // For latency-sensitive scenarios
    )
}

Callback Signature

func(response resp.Value)

// Check for errors
if response.Error() != nil {
    // Handle error
}

// Get values
response.Integer()   // int
response.String()    // string
response.Bool()      // bool
response.Array()     // []resp.Value
response.Bytes()     // []byte

Available Commands

Key Operations

redis.Del(key, callback)
redis.Exists(key, callback)
redis.Expire(key, ttlSeconds, callback)
redis.Persist(key, callback)

String Operations

redis.Get(key, callback)
redis.Set(key, value, callback)
redis.SetEx(key, value, ttlSeconds, callback)
redis.SetNX(key, value, ttlSeconds, callback)  // ttl=0 means no expiry
redis.MGet(keys, callback)
redis.MSet(kvMap, callback)
redis.Incr(key, callback)
redis.Decr(key, callback)
redis.IncrBy(key, delta, callback)
redis.DecrBy(key, delta, callback)

List Operations

redis.LLen(key, callback)
redis.RPush(key, values, callback)
redis.RPop(key, callback)
redis.LPush(key, values, callback)
redis.LPop(key, callback)
redis.LIndex(key, index, callback)
redis.LRange(key, start, stop, callback)
redis.LRem(key, count, value, callback)
redis.LInsertBefore(key, pivot, value, callback)
redis.LInsertAfter(key, pivot, value, callback)

Hash Operations

redis.HExists(key, field, callback)
redis.HDel(key, fields, callback)
redis.HLen(key, callback)
redis.HGet(key, field, callback)
redis.HSet(key, field, value, callback)
redis.HMGet(key, fields, callback)
redis.HMSet(key, kvMap, callback)
redis.HKeys(key, callback)
redis.HVals(key, callback)
redis.HGetAll(key, callback)
redis.HIncrBy(key, field, delta, callback)
redis.HIncrByFloat(key, field, delta, callback)

Set Operations

redis.SCard(key, callback)
redis.SAdd(key, values, callback)
redis.SRem(key, values, callback)
redis.SIsMember(key, value, callback)
redis.SMembers(key, callback)
redis.SDiff(key1, key2, callback)
redis.SDiffStore(dest, key1, key2, callback)
redis.SInter(key1, key2, callback)
redis.SInterStore(dest, key1, key2, callback)
redis.SUnion(key1, key2, callback)
redis.SUnionStore(dest, key1, key2, callback)

Sorted Set Operations

redis.ZCard(key, callback)
redis.ZAdd(key, memberScoreMap, callback)
redis.ZCount(key, min, max, callback)
redis.ZIncrBy(key, member, delta, callback)
redis.ZScore(key, member, callback)
redis.ZRank(key, member, callback)
redis.ZRevRank(key, member, callback)
redis.ZRem(key, members, callback)
redis.ZRange(key, start, stop, callback)
redis.ZRevRange(key, start, stop, callback)

Lua Script

redis.Eval(script, numkeys, keys, args, callback)

Raw Command

redis.Command([]interface{}{"SET", "key", "value"}, callback)

Rate Limiting Example

func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig) types.Action {
    now := time.Now()
    minuteAligned := now.Truncate(time.Minute)
    timeStamp := strconv.FormatInt(minuteAligned.Unix(), 10)
    
    err := config.redis.Incr(timeStamp, func(response resp.Value) {
        if response.Error() != nil {
            log.Errorf("redis error: %v", response.Error())
            proxywasm.ResumeHttpRequest()
            return
        }
        
        count := response.Integer()
        ctx.SetContext("timeStamp", timeStamp)
        ctx.SetContext("callTimeLeft", strconv.Itoa(config.qpm - count))
        
        if count == 1 {
            // First request in this minute, set expiry
            config.redis.Expire(timeStamp, 60, func(response resp.Value) {
                if response.Error() != nil {
                    log.Errorf("expire error: %v", response.Error())
                }
                proxywasm.ResumeHttpRequest()
            })
        } else if count > config.qpm {
            proxywasm.SendHttpResponse(429, [][2]string{
                {"timeStamp", timeStamp},
                {"callTimeLeft", "0"},
            }, []byte("Too many requests\n"), -1)
        } else {
            proxywasm.ResumeHttpRequest()
        }
    })
    
    if err != nil {
        log.Errorf("redis call failed: %v", err)
        return types.HeaderContinue
    }
    return types.HeaderStopAllIterationAndWatermark
}

func onHttpResponseHeaders(ctx wrapper.HttpContext, config MyConfig) types.Action {
    if ts := ctx.GetContext("timeStamp"); ts != nil {
        proxywasm.AddHttpResponseHeader("timeStamp", ts.(string))
    }
    if left := ctx.GetContext("callTimeLeft"); left != nil {
        proxywasm.AddHttpResponseHeader("callTimeLeft", left.(string))
    }
    return types.HeaderContinue
}

Important Notes

  1. Check Ready() - redis.Ready() returns false if init failed
  2. Auto-reconnect - Client handles NOAUTH errors and re-authenticates automatically
  3. Buffering - Default 3ms flush timeout and 1024 byte buffer; use WithDisableBuffer() for latency-sensitive scenarios
  4. Error handling - Always check response.Error() in callbacks