Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit 8bf2ea8

Browse files
committed
Add more LUA docs
1 parent 725137e commit 8bf2ea8

File tree

1 file changed

+113
-6
lines changed

1 file changed

+113
-6
lines changed

README.md

Lines changed: 113 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ we've added a new
261261
[Getting connected to Azure Redis via SSL](https://github.com/ServiceStack/ServiceStack/wiki/Secure-SSL-Redis-connections-to-Azure-Redis)
262262
to help you get started.
263263

264-
## New Generic API's for calling Custom Redis commands
264+
## Generic APIs for calling Custom Redis commands
265265

266266
Most of the time when waiting to use a new [Redis Command](http://redis.io/commands) you'll need to wait for an updated version of
267267
**ServiceStack.Redis** to add support for the new commands likewise there are times when the Redis Client doesn't offer every permutation
@@ -328,7 +328,21 @@ Poco dto = ret.GetResult<Poco>();
328328
dto.Name.Print(); // Bar
329329
```
330330

331-
## New Managed Pub/Sub Server
331+
This API is used in most of Redis React UI's
332+
[redis.js](https://github.com/ServiceStackApps/RedisReact/blob/master/src/RedisReact/RedisReact/js/redis.js)
333+
JavaScript client library where Redis server commands are made available via the
334+
[single ServiceStack Service](https://github.com/ServiceStackApps/RedisReact/blob/a1b66603d52d2f18b96227fc455ecb5323e424c8/src/RedisReact/RedisReact.ServiceInterface/RedisServices.cs#L73):
335+
336+
```csharp
337+
public object Any(CallRedis request)
338+
{
339+
var args = request.Args.ToArray();
340+
var response = new CallRedisResponse { Result = Redis.Custom(args) };
341+
return response;
342+
}
343+
```
344+
345+
## Managed Pub/Sub Server
332346

333347
The Pub/Sub engine powering
334348
[Redis ServerEvents](https://github.com/ServiceStack/ServiceStack/wiki/Redis-Server-Events) and
@@ -400,7 +414,7 @@ var redisPubSub = new RedisPubSubServer(clientsManager, "channel-1", "channel-2"
400414
Calling `Start()` after it's initialized will get it to start listening and processing any messages
401415
published to the subscribed channels.
402416

403-
### New Lex Operations
417+
### Lex Operations
404418

405419
The new [ZRANGEBYLEX](http://redis.io/commands/zrangebylex) sorted set operations allowing you to query a sorted set lexically have been added.
406420
A good showcase for this is available on [autocomplete.redis.io](http://autocomplete.redis.io/).
@@ -446,7 +460,7 @@ Redis.SearchSortedSetCount("zset", "(a", "(c")
446460

447461
More API examples are available in [LexTests.cs](https://github.com/ServiceStack/ServiceStack.Redis/blob/master/tests/ServiceStack.Redis.Tests/LexTests.cs).
448462

449-
### New HyperLog API
463+
### HyperLog API
450464

451465
The development branch of Redis server (available when v3.0 is released) includes an ingenious algorithm to approximate the unique elements in a set with maximum space and time efficiency. For details about how it works see Redis's creator Salvatore's blog who [explains it in great detail](http://antirez.com/news/75). Essentially it lets you maintain an efficient way to count and merge unique elements in a set without having to store its elements.
452466
A Simple example of it in action:
@@ -463,7 +477,7 @@ redis.MergeHyperLogs("mergedset", "set1", "set2");
463477
var mergeCount = redis.CountHyperLog("mergedset"); //6
464478
```
465479

466-
### New Scan APIs Added
480+
### Scan APIs
467481

468482
Redis v2.8 introduced a beautiful new [SCAN](http://redis.io/commands/scan) operation that provides an optimal strategy for traversing a redis instance entire keyset in managable-size chunks utilizing only a client-side cursor and without introducing any server state. It's a higher performance alternative and should be used instead of [KEYS](http://redis.io/commands/keys) in application code. SCAN and its related operations for traversing members of Sets, Sorted Sets and Hashes are now available in the Redis Client in the following API's:
469483

@@ -495,15 +509,108 @@ var scanUsers = Redis.ScanAllKeys("urn:User:*");
495509
var sampleUsers = scanUsers.Take(10000).ToList(); //Stop after retrieving 10000 user keys
496510
```
497511

512+
### Efficient SCAN in LUA
513+
514+
The C# API below returns the first 10 results matching the `key:*` pattern:
515+
516+
```csharp
517+
var keys = Redis.ScanAllKeys(pattern: "key:*", pageSize: 10)
518+
.Take(10).ToList();
519+
```
520+
521+
However the C# Streaming API above requires an unknown number of Redis Operations (bounded to the number of keys in Redis)
522+
to complete the request. The number of SCAN calls can be reduced by choosing a higher `pageSize` to tell Redis to scan more keys
523+
each time the SCAN operation is called.
524+
525+
As the number of API calls has the potential to result in a large number of Redis Operations, it can end up yielding an unacceptable
526+
delay due to the latency of multiple dependent remote network calls. An easy solution is to instead have the multiple SCAN calls
527+
performed in-process on the Redis Server, eliminating the network latency of multiple SCAN calls, e.g:
528+
529+
```csharp
530+
const string FastScanScript = @"
531+
local limit = tonumber(ARGV[2])
532+
local pattern = ARGV[1]
533+
local cursor = 0
534+
local len = 0
535+
local results = {}
536+
repeat
537+
local r = redis.call('scan', cursor, 'MATCH', pattern, 'COUNT', limit)
538+
cursor = tonumber(r[1])
539+
for k,v in ipairs(r[2]) do
540+
table.insert(results, v)
541+
len = len + 1
542+
if len == limit then break end
543+
end
544+
until cursor == 0 or len == limit
545+
return results";
546+
547+
RedisText r = redis.ExecLua(FastScanScript, "key:*", "10");
548+
r.Children.Count.Print() //= 10
549+
```
550+
551+
The `ExecLua` API returns this complex LUA table response in the `Children` collection of the `RedisText` Response.
552+
553+
#### Alternative Complex API Response
554+
555+
Another way to return complex data structures in a LUA operation is to serialize the result as JSON
498556

499-
### New IRedisClient LUA API's
557+
return cjson.encode(results)
558+
559+
Which you can access as raw JSON by parsing the response as a String with:
560+
561+
```csharp
562+
string json = redis.ExecLuaAsString(FastScanScript, "key:*", "10");
563+
```
564+
565+
> This is also the approach used in Redis React's
566+
[RedisServices](https://github.com/ServiceStackApps/RedisReact/blob/a1b66603d52d2f18b96227fc455ecb5323e424c8/src/RedisReact/RedisReact.ServiceInterface/RedisServices.cs#L60).
567+
568+
### ExecCachedLua
569+
570+
ExecCachedLua is a convenient high-level API that eliminates the bookkeeping required for executing high-performance server LUA
571+
Scripts which suffers from many of the problems that RDBMS stored procedures have which depends on pre-existing state in the RDBMS
572+
that needs to be updated with the latest version of the Stored Procedure.
573+
574+
With Redis LUA you either have the option to send, parse, load then execute the entire LUA script each time it's called or
575+
alternatively you could pre-load the LUA Script into Redis once on StartUp and then execute it using the Script's SHA1 hash.
576+
The issue with this is that if the Redis server is accidentally flushed you're left with a broken application relying on a
577+
pre-existing script that's no longer there. The new `ExecCachedLua` API provides the best of both worlds where it will always
578+
execute the compiled SHA1 script, saving bandwidth and CPU but will also re-create the LUA Script if it no longer exists.
579+
580+
You can instead execute the compiled LUA script above by its SHA1 identifier, which continues to work regardless if it never existed
581+
or was removed at runtime, e.g:
582+
583+
```csharp
584+
// #1: Loads LUA script and caches SHA1 hash in Redis Client
585+
r = redis.ExecCachedLua(FastScanScript, sha1 =>
586+
redis.ExecLuaSha(sha1, "key:*", "10"));
587+
588+
// #2: Executes using cached SHA1 hash
589+
r = redis.ExecCachedLua(FastScanScript, sha1 =>
590+
redis.ExecLuaSha(sha1, "key:*", "10"));
591+
592+
// Deletes all existing compiled LUA scripts
593+
redis.ScriptFlush();
594+
595+
// #3: Executes using cached SHA1 hash, gets NOSCRIPT Error, re-creates and re-executes with SHA1 hash
596+
r = redis.ExecCachedLua(FastScanScript, sha1 =>
597+
redis.ExecLuaSha(sha1, "key:*", "10"));
598+
```
599+
600+
### IRedisClient LUA API's
500601

501602
The `IRedisClient` API's for [redis server-side LUA support](http://redis.io/commands/eval) have been re-factored into the more user-friendly API's below:
502603

503604
```csharp
504605
public interface IRedisClient
505606
{
506607
//Eval/Lua operations
608+
T ExecCachedLua<T>(string scriptBody, Func<string, T> scriptSha1);
609+
610+
RedisText ExecLua(string body, params string[] args);
611+
RedisText ExecLua(string luaBody, string[] keys, string[] args);
612+
RedisText ExecLuaSha(string sha1, params string[] args);
613+
RedisText ExecLuaSha(string sha1, string[] keys, string[] args);
507614

508615
string ExecLuaAsString(string luaBody, params string[] args);
509616
string ExecLuaAsString(string luaBody, string[] keys, string[] args);

0 commit comments

Comments
 (0)