Redis (Remote Dictionary Server) is an in-memory data structure project implementing a distributed, in-memory key-value database with optional durability. Redis supports different kinds of abstract data structures, such as strings, lists, maps, sets, sorted sets, HyperLogLogs, bitmaps, streams, and spatial indexes.
You can read more about redis in:

  1. Redis tutorial
  2. Redis traffic stat using tcpdump
  3. Counting Distinct Users In Real Time

1 Setup redis server

Follow the following steps:

Install and Configure Redis

2 Configure redis in wic_redis_server

First of all we access the wic conf wic_redis_server object. There we need to register our redis server.

3 Assign redis server to POS database

Once we entered the redis server, it's time to link the server to a database.
From wic conf wic_database_object object we can access wic_redis_database object and associate our redis server to the database.

4 Master - Slave and Sentinels

Example: Configure a Master - Slave system with 3 Sentinels and link it to a database at wic_conf:

For further replica documentation you can visit redis.io/topics/replication and for more information about the sentinel configuration you can visit redis.io/topics/sentinel.
In this example we will build a structure as the following one:

Master-Slave with 3 Sentinels

Loading...

The only difference between POS_Master and POS_Slave is that the POS_Slave redis.conf file adds the line:
replicaof 192.168.105.173 6379

Once we have finished the master and slave redis.conf files, let's write the sentinel.conf ones: The file looks as following:

Copy
bind 0.0.0.0
protected-mode no
port 26379
daemonize yes
pidfile "/home/redis/sentinel.pid"
logfile "/home/redis/log/sentinel.log"
dir /home/redis
sentinel monitor pos_master_redis 192.168.105.173 6379 2
sentinel down-after-milliseconds pos_master_redis 30000
sentinel parallel-syncs pos_master_redis 1
sentinel failover-timeout pos_master_redis 180000
sentinel deny-scripts-reconfig yes
  • sentinel monitor pos_master_redis 192.168.105.173 6379 2 line is used to tell Redis to monitor a master called pos_master_redis, that is at address 192.168.105.173 and port 6379, with a quorum of 2. Everything is pretty obvious but the quorum argument:
    • The quorum is the number of Sentinels that need to agree about the fact the master is not reachable, in order to really mark the master as failing, and eventually start a failover procedure if possible.
    • However the quorum is only used to detect the failure. In order to actually perform a failover, one of the Sentinels need to be elected leader for the failover and be authorized to proceed. This only happens with the vote of the majority of the Sentinel processes.
  • down-after-milliseconds is the time in milliseconds an instance should not be reachable (either does not reply to our PINGs or it is replying with an error) for a Sentinel starting to think it is down.
  • parallel-syncs sets the number of replicas that can be reconfigured to use the new master after a failover at the same time. The lower the number, the more time it will take for the failover process to complete, however if the replicas are configured to serve old data, you may not want all the replicas to re-synchronize with the master at the same time. While the replication process is mostly non blocking for a replica, there is a moment when it stops to load the bulk data from the master. You may want to make sure only one replica at a time is not reachable by setting this option to the value of 1.
  • If a Sentinel voted another Sentinel for the failover of a given master, it will wait some time to try to failover the same master again. This delay is the failover-timeout. This means that Sentinels will not try to failover the same master at the same time, the first to ask to be authorized will try, if it fails another will try after some time, and so forth.

Now that we have the 5 configuraton files writed (2 redis.conf for the master and slave and 3 sentinel.conf for the 3 sentinels) we need to start the POS_Master.
From 192.168.105.173 machine run:

Copy
redis-server /home/redis/etc/redis.conf

Keep in mind we've located our configuration files at /home/redis/etc/ path. But it can be placed wherever you prefer.

If we take a look to the redis.log file we can see if the master redis started right. If so, execute the same sentence but for the POS_Slave (192.168.105.174). Now we can see in both redis.log files (the master one and the replica one) that syncronization between them has been succeeded.

In order to start a sentinel we can either execute the comand:

Copy
redis-sentinel /home/redis/etc/sentinel.conf

Or:

Copy
redis-server /home/redis/etc/sentinel.conf --sentinel

If everything goes well, we will see information of the sentinel in its log file such as pid, port, sentinel id, which master and which slave are being monitored...
After starting the second sentinel, the log file will be like the one before except for the fact that information about the other sentinel will apear (the first sentinel log file will also be updated with information of the second one).

Finally, start the 3rd sentinel as we have done with the other two (the log files will also reflect the 3rd sentinel information). If we look again at the end of the sentinel.config file, we can appreciate that a few lines containing the information of the replica and the other sentinels have been added.

Now it's time to define our redis in the wic_conf at table wic_redis_server. The only difference between this case and the one where we only have a simple redis, is that now we need to inform the redis_sentinels column (sting containing ip:port divided by | ) instead of the redis_host and redis_port columns.
In that case: 192.168.105.173:26379|192.168.105.174:26379|192.168.105.175:26379

The Redis Code introduced at wic_conf must be the same as the code we previously have said the sentinel to monitor.

We can start redis and sentinels as services:

Let's connect to one sentinel, for example POS_Sentinel3. From our terminal execute:

Copy
redis-cli -h 192.168.105.175 -p 26379;

Now we can ask, for example, which is the ip and port of the master:

Copy
SENTINEL get-master-addr-by-name pos_master_redis
1) "192.168.105.173"
2) "6379"

Now if we kill the master redis, after a few seconds we can see that sentinel.log file reflects some information about the shutdown of the master and switched the master to the slave: switch-master pos_master_redis 192.168.105.173 6379 192.168.105.174 6379. So slave has become master and master has become slave.
If we run again the command to the sentinel asking for the master information, we can see it changed to 192.168.105.174 : 6379 :

Copy
SENTINEL get-master-addr-by-name pos_master_redis
1) "192.168.105.174"
2) "6379"

Another useful command is:

Copy
SENTINEL ckquorum pos_master_redis
  • With all 3 sentinels up, response is:
    OK 3 usable Sentinels. Quorum and failover authorization can be reached
  • With 2 sentinels up and 1 down, response is:
    OK 2 usable Sentinels. Quorum and failover authorization can be reached
  • With only 1 sentinel up (the one we are querying to) and the other 2 down, response is:
    (error) NOQUORUM 1 usable Sentinels. Not enough available Sentinels to reach the specified quorum for this master. Not enough available Sentinels to reach the majority and authorize a failover

5 Check redis status

In order to verify that redis is working properly we connect to it:

Copy
redis-cli -h 192.168.105.173

And run the following command:

Copy
ping

The response if redis is up will be :

Copy
PONG

5.1 From DB Studio

We can also verify redis status from DB Studio. Execute the following command to ensure there is a redis configured in wic_conf for the database. The output will be a boolean value, if it's true it means that the redis exists in the database parametrization.

Copy
return Ax.ext.db.existRedis();

If the redis is configured in wic_conf, we can execute the following command to know if redis is ready.

Copy
return Ax.ext.db.getRedisClient().isReady();

5.2 From redis object

We can check the redis status in the pos_redis_status object. There we can see if redis exists, if redis is ready and which indexes, suggestions and synonyms are created.

This object also informs us about how many products should be loaded to each redis index and how many products are loaded.

6 Transfer data to redis

In the POS (either ecom or shop) application we use redis indexes and suggestions to speed up the catalog visualization. In order to transfer the data from the database to the redis we need to execute certain JS scripts cataloged at wic_pos.
When one of the scripts is executed it insert or updates the row at pos_redis_indexes, this table is the one that indicates us the name of the redis indexes and the last execution time. The scripts also insert a row at table pos_redis_update_log with a minimal log of the execution.

This processes are automated and we can load the products data using the pos_redis_indexes object buttons.

The button will ask us for some parameters:

  • Catalog code
  • Language (only in the suggestion button)
  • If we want a full load or not
  • Product id (if we inform a product id, only that product will be updated at the redis database)

Note

Main product management has button load to redis to load the current product information to redis. This function is used to load the information of one single product just after update it.

6.1 Products and specific prices

The JS script we need to call to transfer the product data is the one named redis_prod_sp.

When a product is updated, the last_update column from pos_product table is updated. This script deletes from Redis all the products that were modified after last execution of the script and upload the information of the product again. Doing that we only update products that were modified instead of deleting and inserting them all each time.

Copy
Ax.db.call('redis_prod_sp',catalogCode, fullLoad, comesFromCron, product_id);
  • catalogCode (String): Indicates the code of the catalog we want to load.
  • fullLoad (Boolean):
    • true: Deletes all products from redis for the specified catalog and load it all again.
    • false: Only delete and upload the products modified since last redis execution.
  • comesFromCron (Boolean):
    • true: The script is called from a cron process.
    • false: The script is not called from cron.
  • product_id (Integer | null): Id of the product we want to update (if this field is not null, the fullLoad parameter must be false)

Take in mind that in order to load the synonyms we need to do a full load. This is beacuse Redis does not accept the delete of synonyms, only the addition. The chain of events is like: Create REDIS index --> Load syonyms data --> Load products

6.2 Suggestion

Suggestion is the one in charge to show the drop down list in the search bar.

We insert into the suggestion the product name and each one of the words that compose the name (e.g. The product Red Balloon will insert 3 words to the suggestion: Red , Balloon and Red Balloon)
The script we need to call to build the suggestion is the one named redis_sugget_products.

Copy
Ax.db.call('redis_sugget_products',catalogCode, langCode, fullLoad, comesFromCron, productId);
  • catalogCode (String): Indicates the code of the catalog we want to load.
  • langCode (String | null): Indicates the code of the language we want to upload. If null, it uploads the non-idiomatic suggestions.
  • fullLoad (Boolean):
    • true: Deletes all products from redis for the specified catalog and load it all again.
    • false: Only delete and upload the products modified since last redis execution.
  • comesFromCron (Boolean):
    • true: The script is called from a cron process.
    • false: The script is not called from cron.
  • product_id (Integer | null): Id of the product we want to update (if this field is not null, the fullLoad parameter must be false)

We can do a full load or not. If the load is not full, the index does not change and the current used index is the one modified. On the other hand if the load is a full load , the script alternates between 2 suggestions: Imagine they are called demo_pos_sugget0_A and demo_pos_sugget1_A, pos_redis_indexes table will tell us which one is the active one. Lets suppose the active one is demo_pos_sugget0_A. When the script recalculates the suggestion, it will drop the demo_pos_sugget1_A (the one we are not using) and insert the values into that suggestion, then it will update the row from pos_redis_indexes indicating that from now on the suggestion we need to point is the demo_pos_sugget1_A. If we call the script again, the process will be the same but the other way (demo_pos_sugget0_A will be dropped, recalculated and will be the one that we need to point).