XCHANGE
: the root of the location where the Smithsonian/xchange library is installed. It expects to find xchange.h
under $(XCHANGE)/include
and libxchange.so
under $(XCHANGE)/lib
or else in the default LD_LIBRARY_PATH
.
CC
: The C compiler to use (default: gcc
).
-CPPFLAGS
: C pre-processor flags, such as externally defined compiler constants.
+CPPFLAGS
: C preprocessor flags, such as externally defined compiler constants.
CFLAGS
: Flags to pass onto the C compiler (default: -Os -Wall -std=c99
). Note, -Iinclude
will be added automatically.
LDFLAGS
: Extra linker flags (default is not set). Note, -lm -lxchange
will be added automatically.
BUILD_MODE
: You can set it to debug
to enable debugging features: it will initialize the global xDebug
variable to TRUE
and add -g
to CFLAGS
.
@@ -348,7 +348,7 @@
int redisxCheckDestroyRESP(RESP *resp, char expectedType, int expectedSize)
Definition redisx.c:445
void redisxDestroyRESP(RESP *resp)
Definition redisx.c:391
#define RESP_ARRAY
RESP array type.
Definition redisx.h:88
-
Before destroying a RESP structure, the caller may want to de-reference values within it if they are to be used as is (without making copies), e.g.:
+Before destroying a RESP structure, the caller may want to dereference values within it if they are to be used as is (without making copies), e.g.:
char *stringValue = NULL;
@@ -378,7 +378,7 @@
Getting and setting keyed values
-
Key/value pairs are the bread and butter of Redis. They come in two variaties: (1) there are top-level key-value pairs, and (2) there are key-value pairs organized into hash tables, where the table name is a top-level key, but the fields in the table are not. The RedisX library offers a unified approach for dealing with key/value pairs, whether they are top level or hash-tables. Simply, a table name NULL
is used to refer to top-level keys.
+
Key/value pairs are the bread and butter of Redis. They come in two varieties: (1) there are top-level key-value pairs, and (2) there are key-value pairs organized into hash tables, where the table name is a top-level key, but the fields in the table are not. The RedisX library offers a unified approach for dealing with key/value pairs, whether they are top level or hash-tables. Simply, a table name NULL
is used to refer to top-level keys.
Retrieving individual keyed values is simple:
int len;
@@ -425,14 +425,14 @@
int nMatches;
int status;
-
+
if (status != X_SUCCESS) {
...
}
-
+
...
@@ -449,14 +449,14 @@
...
}
-
+
...
redisxDestroyEntries(entries, nMatches);
RedisEntry * redisxScanTable(Redis *redis, const char *table, const char *pattern, int *n, int *status)
Definition redisx-tab.c:728
- Finally, you may use redisxSetScanCount()
to tune just how many results should individial scan queries should return (but only if you are really itching to tweak it). Please refer to the Redis documentation on the behavior of the SCAN
and HSCAN
commands to learn more.
+
Finally, you may use redisxSetScanCount()
to tune just how many results should individual scan queries should return (but only if you are really itching to tweak it). Please refer to the Redis documentation on the behavior of the SCAN
and HSCAN
commands to learn more.
@@ -475,7 +475,7 @@
int status =
redisxPublish(redis,
"hello_channel",
"Hello world!", 0);
int redisxPublish(Redis *redis, const char *channel, const char *data, int length)
Definition redisx-sub.c:131
The last argument is an optional string length, if readily available, or if sending a substring only, or else a string or byte sequence that is not null-terminated. If zero is used for the length, as in the example above, it will automatically determine the length of the 0-terminated string message using strlen()
.
-Alternatively, you may use the redisxPublishAsync()
instead if you want to publish on a subscription client to which you have already have exlusive access (e.g. after an appropriate redisxLockConnected()
call).
+Alternatively, you may use the redisxPublishAsync()
instead if you want to publish on a subscription client to which you have already have exclusive access (e.g. after an appropriate redisxLockConnected()
call).
Subscriptions
@@ -485,7 +485,7 @@
printf("Incoming message on channel %s: %s\n", channel, msg == NULL ? "<null>" : msg);
}
-There are some basic rules (best practices) for message processing. They should be fast, and never block for extended periods. If extensive processing is required, or may need to wait extensively for some resource or mutex locking, then its best that the processing function simply places the incoming message onto a queue, and let a separate background thread to the heavy lifting without holding up the subsription processing of other callback routines.
+There are some basic rules (best practices) for message processing. They should be fast, and never block for extended periods. If extensive processing is required, or may need to wait extensively for some resource or mutex locking, then its best that the processing function simply places the incoming message onto a queue, and let a separate background thread to the heavy lifting without holding up the subscription processing of other callback routines.
Also, it is important that the call should never attempt to modify or call free()
on the supplied string arguments, since that would interfere with other subscriber calls.
Once the function is defined, you can activate it via:
@@ -558,11 +558,11 @@
int redisxSendRequestAsync(RedisClient *cl, const char *command, const char *arg1, const char *arg2, const char *arg3)
Definition redisx-client.c:538
@ REDISX_INTERACTIVE_CHANNEL
Redis channel number for interactive queries.
Definition redisx.h:109
Structure that represents a single Redis client connection instance.
Definition redisx.h:146
-
If at any point things don't go according to plan in the middle of the block, you can call redisAbortBlockAsync()
to abort and discard all prior commands submitted in the execution block already. It is important to remembet that every time you call redisxStartBlockAsync()
, you must call either redisxExecBlockAsync()
to execute it or else redisxAbortBlockAsync()
to discard it. Failure to do so, will effectively end you up with a hung Redis client.
+If at any point things don't go according to plan in the middle of the block, you can call redisAbortBlockAsync()
to abort and discard all prior commands submitted in the execution block already. It is important to remember that every time you call redisxStartBlockAsync()
, you must call either redisxExecBlockAsync()
to execute it or else redisxAbortBlockAsync()
to discard it. Failure to do so, will effectively end you up with a hung Redis client.
LUA script loading and execution
-LUA scripting offers a more capable version of executing complex routines on the Redis server. LUA is a scripting language akin to python, and allows you to add extra logic, string manipulation etc. to your Redis queries. Best of all, once you upload the script to the server, it can reduce network traffic significantly by not having to repeatedly submit the same set of Redis commands every single time. LUA scipts also get executed very efficiently on the server, and produce only the result you want/need.
+LUA scripting offers a more capable version of executing complex routines on the Redis server. LUA is a scripting language akin to python, and allows you to add extra logic, string manipulation etc. to your Redis queries. Best of all, once you upload the script to the server, it can reduce network traffic significantly by not having to repeatedly submit the same set of Redis commands every single time. LUA scripts also get executed very efficiently on the server, and produce only the result you want/need.
Assuming you have prepared your LUA script appropriately, you can upload it to the Redis server as:
char *script = ...
@@ -574,7 +574,7 @@
...
}
- Redis will refer to the script by its SHA1 sum, so it's important keep a record of it. You'll call the script with its SHA1 sum, a set of redis keys the script may use, and a set of other parameters it might need.
+Redis will refer to the script by its SHA1 sum, so it's important keep a record of it. You'll call the script with its SHA1 sum, a set of Redis keys the script may use, and a set of other parameters it might need.
char *keyArgs[] = { "my-redis-key-argument", NULL };
@@ -672,7 +672,7 @@
redisxSetPipelineConsumer(redis, my_resp_processor);
-
Request are sent via the redisxSendRequestAsync()
and redisxSendArrayRequestAsync()
functions. Note again, the Async
naming, which indicates the asynchronous nature of this calls – and which suggests that these should be called with the approrpiate mutex locked to prevent concurrency issues and to maintain a predictable order (very important!) for processing the responses.
+
Request are sent via the redisxSendRequestAsync()
and redisxSendArrayRequestAsync()
functions. Note again, the Async
naming, which indicates the asynchronous nature of this calls – and which suggests that these should be called with the appropriate mutex locked to prevent concurrency issues and to maintain a predictable order (very important!) for processing the responses.
@@ -711,7 +711,7 @@
It is up to you and your callback function to keep track of what responses are expected and in what order. Some best practices to help deal with pipeline responses are summarized here:
- Use
redisxSkipReplyAsync()
prior to sending pipeline requests for which you do not need a response. (This way your callback does not have to deal with unnecessary responses at all.
-- For requests that return a value, keep a record (in a FIFO) of the expected types and your data that depends on the content of the responses. For example, for pipelined
HGET
commands, your FIFO should have a record that specifies that a bulk string response is expected, and a pointer to data which is used to store the returned value – so that you pipeline response processing callback function can check that the reponse is the expected type (and size) and knows to assign/process the response appropriately to your application data.
+- For requests that return a value, keep a record (in a FIFO) of the expected types and your data that depends on the content of the responses. For example, for pipelined
HGET
commands, your FIFO should have a record that specifies that a bulk string response is expected, and a pointer to data which is used to store the returned value – so that you pipeline response processing callback function can check that the response is the expected type (and size) and knows to assign/process the response appropriately to your application data.
- You may insert Redis
PING
/ECHO
commands to section your responses, or to provide directives to your pipeline response processor function. You can tag them uniquely so that the echoed responses can be parsed and interpreted by your callback function. For example, you may send a PING
/ECHO
commands to Redis with the tag "@my_resp_processor: START sequence A"
, or something else meaningful that you can uniquely distinguish from all other responses that you might receive.
RedisX optimizes the pipeline client for high throughput (bandwidth), whereas the interactive and subscription clients are optimized for low-latency, at the socket level.
@@ -757,7 +757,7 @@
- TLS support (perhaps...)
- Add high-level support for managing and calling custom Redis functions.
- Add support for
CLIENT TRACKING
/ CLIENT CACHING
.
-
- Add more high-level redis commands, e.g. for lists, streams, etc.
+
- Add more high-level Redis commands, e.g. for lists, streams, etc.
If you have an idea for a must have feature, please let me (Attila) know. Pull requests, for new features or fixes to existing ones, are especially welcome!
diff --git a/doc/README.md b/doc/README.md
index 7117575..66f8015 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -52,7 +52,7 @@ Before then the API may undergo slight changes and tweaks. Use the repository as
- [Redis commands](https://redis.io/docs/latest/commands/) (reference documentation)
- [SMA eXchange (SMA-X)](https://docs.google.com/document/d/1eYbWDClKkV7JnJxv4MxuNBNV47dFXuUWu7C4Ve_YTf0/edit?usp=sharing)
- -- A structured realtime database built on Redis.
+ -- A structured realtime database built on Redis / Valkey.
* [Smithsonian/smax-server](https://github.com/Smithsonian/smax-server) -- SMA-X server configuration kit
* [Smithsonian/smax-clib](https://github.com/Smithsonian/smax-clib) -- A C/C++ client library and toolkit to SMA-X,
based on __RedisX__
@@ -83,7 +83,7 @@ prior to invoking `make`. The following build variables can be configured:
- `CC`: The C compiler to use (default: `gcc`).
- - `CPPFLAGS`: C pre-processor flags, such as externally defined compiler constants.
+ - `CPPFLAGS`: C preprocessor flags, such as externally defined compiler constants.
- `CFLAGS`: Flags to pass onto the C compiler (default: `-Os -Wall -std=c99`). Note, `-Iinclude` will be added
automatically.
@@ -340,8 +340,8 @@ two steps may be combined to automatically discard invalid or unexpected `RESP`
}
```
-Before destroying a RESP structure, the caller may want to de-reference values within it if they are to be used
-as is (without making copies), e.g.:
+Before destroying a RESP structure, the caller may want to dereference values within it if they are to be used as is
+(without making copies), e.g.:
```c
@@ -376,7 +376,7 @@ as is (without making copies), e.g.:
### Getting and setting keyed values
-Key/value pairs are the bread and butter of Redis. They come in two variaties: (1) there are top-level key-value
+Key/value pairs are the bread and butter of Redis. They come in two varieties: (1) there are top-level key-value
pairs, and (2) there are key-value pairs organized into hash tables, where the table name is a top-level key, but the
fields in the table are not. The RedisX library offers a unified approach for dealing with key/value pairs, whether
they are top level or hash-tables. Simply, a table name `NULL` is used to refer to top-level keys.
@@ -469,14 +469,14 @@ something like:
int nMatches; // We'll return the number of matching Redis keys here...
int status; // We'll return the error status here...
- // Return all redis keywords starting with "system:"
+ // Return all Redis top-level keywords starting with "system:"
char **keys = redisxScanKeys(redis, "system:*", &nMatches, &status);
if (status != X_SUCCESS) {
// Oops something went wrong...
...
}
- // Use 'keys' as appropriate, possibly de-referencing values we want to
+ // Use 'keys' as appropriate, possibly dereferencing values we want to
// retain in other persistent data structures...
...
@@ -496,7 +496,7 @@ Or, to retrieve the values from a hash table for a set of keywords that match a
...
}
- // Use 'entries' as appropriate, possibly de-referencing values we want to
+ // Use 'entries' as appropriate, possibly dereferencing values we want to
// retain in other persistent data structures...
...
@@ -504,7 +504,7 @@ Or, to retrieve the values from a hash table for a set of keywords that match a
redisxDestroyEntries(entries, nMatches);
```
-Finally, you may use `redisxSetScanCount()` to tune just how many results should individial scan queries should return
+Finally, you may use `redisxSetScanCount()` to tune just how many results should individual scan queries should return
(but only if you are really itching to tweak it). Please refer to the Redis documentation on the behavior of the
`SCAN` and `HSCAN` commands to learn more.
@@ -533,7 +533,7 @@ or byte sequence that is not null-terminated. If zero is used for the length, as
automatically determine the length of the 0-terminated string message using `strlen()`.
Alternatively, you may use the `redisxPublishAsync()` instead if you want to publish on a subscription client to which
-you have already have exlusive access (e.g. after an appropriate `redisxLockConnected()` call).
+you have already have exclusive access (e.g. after an appropriate `redisxLockConnected()` call).
### Subscriptions
@@ -558,7 +558,7 @@ Here is an example `RedisSubscriberCall` implementation to process messages:
There are some basic rules (best practices) for message processing. They should be fast, and never block for extended
periods. If extensive processing is required, or may need to wait extensively for some resource or mutex locking, then
its best that the processing function simply places the incoming message onto a queue, and let a separate background
-thread to the heavy lifting without holding up the subsription processing of other callback routines.
+thread to the heavy lifting without holding up the subscription processing of other callback routines.
Also, it is important that the call should never attempt to modify or call `free()` on the supplied string arguments,
since that would interfere with other subscriber calls.
@@ -652,7 +652,7 @@ atomically. Such an execution block in RedisX may look something like:
```
If at any point things don't go according to plan in the middle of the block, you can call `redisAbortBlockAsync()` to
-abort and discard all prior commands submitted in the execution block already. It is important to remembet that every
+abort and discard all prior commands submitted in the execution block already. It is important to remember that every
time you call `redisxStartBlockAsync()`, you must call either `redisxExecBlockAsync()` to execute it or else
`redisxAbortBlockAsync()` to discard it. Failure to do so, will effectively end you up with a hung Redis client.
@@ -663,7 +663,7 @@ time you call `redisxStartBlockAsync()`, you must call either `redisxExecBlockAs
[LUA](https://www.lua.org/) scripting offers a more capable version of executing complex routines on the Redis server.
LUA is a scripting language akin to python, and allows you to add extra logic, string manipulation etc. to your Redis
queries. Best of all, once you upload the script to the server, it can reduce network traffic significantly by not
-having to repeatedly submit the same set of Redis commands every single time. LUA scipts also get executed very
+having to repeatedly submit the same set of Redis commands every single time. LUA scripts also get executed very
efficiently on the server, and produce only the result you want/need.
Assuming you have prepared your LUA script appropriately, you can upload it to the Redis server as:
@@ -682,7 +682,7 @@ Assuming you have prepared your LUA script appropriately, you can upload it to t
```
Redis will refer to the script by its SHA1 sum, so it's important keep a record of it. You'll call the script with
-its SHA1 sum, a set of redis keys the script may use, and a set of other parameters it might need.
+its SHA1 sum, a set of Redis keys the script may use, and a set of other parameters it might need.
```c
Redis *redis = ...
@@ -833,7 +833,7 @@ Before sending the pipelined requests, the user first needs to specify the funct
Request are sent via the `redisxSendRequestAsync()` and `redisxSendArrayRequestAsync()` functions. Note again, the
`Async` naming, which indicates the asynchronous nature of this calls -- and which suggests that these should be called
-with the approrpiate mutex locked to prevent concurrency issues and to maintain a predictable order (very important!)
+with the appropriate mutex locked to prevent concurrency issues and to maintain a predictable order (very important!)
for processing the responses.
```c
@@ -888,7 +888,7 @@ practices to help deal with pipeline responses are summarized here:
- For requests that return a value, keep a record (in a FIFO) of the expected types and your data that depends on
the content of the responses. For example, for pipelined `HGET` commands, your FIFO should have a record that
specifies that a bulk string response is expected, and a pointer to data which is used to store the returned value
- -- so that you pipeline response processing callback function can check that the reponse is the expected type
+ -- so that you pipeline response processing callback function can check that the response is the expected type
(and size) and knows to assign/process the response appropriately to your application data.
- You may insert Redis `PING`/`ECHO` commands to section your responses, or to provide directives to your pipeline
@@ -972,11 +972,12 @@ Some obvious ways the library could evolve and grow in the not too distant futur
- Automated regression testing and coverage tracking.
- Keep track of subscription patterns, and automatically resubscribe to them on reconnecting.
- Support for the [RESP3](https://github.com/antirez/RESP3/blob/master/spec.md) standard and Redis `HELLO`.
- - Support for [Redis Sentinel](https://redis.io/docs/latest/develop/reference/sentinel-clients/) clients, for high-availability server configurations.
+ - Support for [Redis Sentinel](https://redis.io/docs/latest/develop/reference/sentinel-clients/) clients, for
+ high-availability server configurations.
- TLS support (perhaps...)
- Add high-level support for managing and calling custom Redis functions.
- Add support for `CLIENT TRACKING` / `CLIENT CACHING`.
- - Add more high-level redis commands, e.g. for lists, streams, etc.
+ - Add more high-level [Redis commands](https://redis.io/docs/latest/commands/), e.g. for lists, streams, etc.
If you have an idea for a must have feature, please let me (Attila) know. Pull requests, for new features or fixes to
existing ones, are especially welcome!