1/*  File:    redis/xgroup.pl
    2    Author:  Roy Ratcliffe
    3    Created: Dec 10 2024
    4    Purpose: Redis XGROUP CREATE command wrapper for Prolog
    5
    6Copyright (c) 2024, Roy Ratcliffe, Northumberland, United Kingdom
    7
    8Permission is hereby granted, free of charge,  to any person obtaining a
    9copy  of  this  software  and    associated   documentation  files  (the
   10"Software"), to deal in  the   Software  without  restriction, including
   11without limitation the rights to  use,   copy,  modify,  merge, publish,
   12distribute, sub-license, and/or sell copies  of   the  Software,  and to
   13permit persons to whom the Software is   furnished  to do so, subject to
   14the following conditions:
   15
   16    The above copyright notice and this permission notice shall be
   17    included in all copies or substantial portions of the Software.
   18
   19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT  WARRANTY OF ANY KIND, EXPRESS
   20OR  IMPLIED,  INCLUDING  BUT  NOT   LIMITED    TO   THE   WARRANTIES  OF
   21MERCHANTABILITY, FITNESS FOR A PARTICULAR   PURPOSE AND NONINFRINGEMENT.
   22IN NO EVENT SHALL THE AUTHORS  OR   COPYRIGHT  HOLDERS BE LIABLE FOR ANY
   23CLAIM, DAMAGES OR OTHER LIABILITY,  WHETHER   IN  AN ACTION OF CONTRACT,
   24TORT OR OTHERWISE, ARISING FROM,  OUT  OF   OR  IN  CONNECTION  WITH THE
   25SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   26
   27*/
   28
   29:- module(redis_xgroup,
   30          [ xgroup_create/3, % +Redis:atom, +Key:atom, +Group:atom
   31            xgroup_create/4  % +Redis:atom, +Key:atom, +Group:atom, +Options:list
   32          ]).   33:- autoload(library(option), [option/2, option/3]).   34:- autoload(library(redis), [redis/3]).

Redis XGROUP CREATE command wrapper

Wraps Redis's `XGROUP CREATE` command with xgroup_create/3 and xgroup_create/4. Translates Prolog options into Redis command arguments, extracts stream id/1 (default $), handles make_stream(true) and entries_read/1, and captures optional reply. Behaves idempotently by catching BUSYGROUP errors.

 xgroup_create(+Redis:atom, +Key:atom, +Group:atom) is det
 xgroup_create(+Redis:atom, +Key:atom, +Group:atom, +Options:list) is det
Creates a consumer group for a Redis stream if it does not already exist. This predicate attempts to create a consumer group named Group for the stream identified by Key in the Redis instance specified by Redis. If the consumer group already exists, the predicate succeeds without error.

The predicate can be called with or without the Options argument. If Options is not provided, it defaults to an empty list. The Options argument allows for additional customisation of the command execution, such as specifying the ID for the consumer group, whether to create the stream if it does not exist, and how many entries to read when the group is created.

Example usage:

% Create a consumer group named "mygroup" for the stream "mystream" in
% the Redis instance "myredis".
?- xgroup_create(myredis, mystream, mygroup).

Delete a consumer group named "mygroup" for the stream "mystream" in the Redis instance "myredis" using:

?- redis(myredis, xgroup(destroy, mystream, mygroup), _).
Arguments:
Redis- The Redis instance identifier.
Key- The Redis stream key.
Group- The name of the consumer group to create.
Options- A list of options to customise the command execution.

Supported options include:

  • id(Id) specifies the ID for the consumer group. Defaults to '$' (the latest entry in the stream).
  • make_stream(true) specifies that the stream should be created if it does not already exist.
  • entries_read(EntriesRead) specifies the number of entries to read when the group is created.
  • reply(Reply) specifies a variable to unify with the command's reply. If not provided, the reply is ignored.
   87xgroup_create(Redis, Key, Group) :- xgroup_create(Redis, Key, Group, []).
   88
   89xgroup_create(Redis, Key, Group, Options) :-
   90    % Construct the XGROUP CREATE command with the provided options. The command
   91    % is built as a Prolog term that will be passed to the redis/3 predicate for
   92    % execution. The options are processed to include the appropriate arguments
   93    % in the command term.
   94    %
   95    % The command structure is as follows:
   96    %
   97    %   XGROUP CREATE key group id|$ [MKSTREAM] [ENTRIESREAD entries-read]
   98    %
   99    (   option(make_stream(true), Options)
  100    ->  Options1 = [mkstream]
  101    ;   Options1 = []
  102    ),
  103    (   option(entries_read(EntriesRead), Options)
  104    ->  Options2 = [entriesread, EntriesRead|Options1]
  105    ;   Options2 = Options1
  106    ),
  107    option(id(Id), Options, $),
  108    Command =.. [xgroup, create, Key, Group, Id|Options2],
  109    option(reply(Reply), Options, _),
  110    catch(redis(Redis, Command, Reply),
  111          % Ignore "BUSYGROUP" error if the group already exists.
  112          % Still throw other errors, if any. This ensures idempotent behaviour.
  113          error(redis_error(busygroup, _), _), true)