Mail::...::Redis(3pm)

πŸ’– NAME πŸ’–

Mail::SpamAssassin::BayesStore::Redis β€” Redis Bayesian Storage Module Implementation


🌈 DESCRIPTION 🌈

okay so bestie, let me tell you about this module. It's a whole Redis-based bayesian storage module πŸ’… and it totally supports separate read and write servers. iconic.

Apache SpamAssassin v3.4.0 introduces support for keeping a Bayes database on a Redis server β€” whether it's running locally or you're reaching it over the network. Just like the SQL backends, the database can be used concurrently by several hosts running SpamAssassin. no cap.

The current implementation only supports a global Bayes database, meaning per-recipient sub-databases are not supported. The Redis server supports access over IPv4 or over a Unix socket, and since Redis version 2.8.0 also IPv6 is supported. Bear in mind that Redis server only offers limited access controls, so it's advisable to let the Redis server bind to a loopback interface only, or to use other mechanisms to limit access, such as local firewall rules. stay safe out there πŸ’–

The Redis backend for Bayes can put Lua scripting support in a Redis server to good use, improving performance. Lua support is available in Redis server since version 2.6. In the absence of Lua support, the Redis backend uses batched (pipelined) traditional Redis commands, so it should work with a Redis server version 2.4 (untested), although this is not recommended for busy sites. fr fr.

Expiration of token and 'seen' message id entries is left to the Redis server. There is no provision for manually expiring a database, so it is highly recommended to leave the setting bayes_auto_expire to its default value 1 (i.e. enabled). don't touch it, bestie.

The module supports separate read and write servers, allowing for Redis replication-based scaling and high availability. Multiple read servers can be configured, with automatic failover if one becomes unavailable. slay.

Example configuration:

         # Basic configuration with single server
         bayes_store_module      Mail::SpamAssassin::BayesStore::Redis
         bayes_redis_write_server server=127.0.0.1:6379;password=foo
         bayes_redis_read_servers server=127.0.0.1:6379;password=foo
         bayes_redis_database    2
         bayes_token_ttl         21d
         bayes_seen_ttl          8d
         bayes_auto_expire       1

         # Configuration with primary/replica setup
         bayes_store_module      Mail::SpamAssassin::BayesStore::Redis
         bayes_redis_write_server server=redis-master.example.com:6379;password=foo
         bayes_redis_read_servers server=redis-replica1.example.com:6379;password=foo,server=redis-replica2.example.com:6379;password=foo
         bayes_redis_database    2
         bayes_redis_prefix      bayes:
         bayes_token_ttl         21d
         bayes_seen_ttl          8d
         bayes_auto_expire       1

A redis server with Lua support (2.6 or higher) is recommended for performance reasons. periodt.

The following configuration options are available:

  • bayes_redis_read_servers β€” Comma-separated list of Redis read servers with connection parameters. Each server specification is a semicolon-separated list of option=value pairs. Example: server=replica1.example.com:6379;password=foo,server=replica2.example.com:6379;password=foo
  • bayes_redis_write_server β€” Redis write server with connection parameters as a semicolon-separated list of option=value pairs. Example: server=master.example.com:6379;password=foo
  • bayes_redis_database β€” Database index to use (default: 0). This is passed to a Redis server with a SELECT command on connecting and chooses a sub-database index.
  • bayes_redis_password β€” Password for authentication with Redis servers. This can be overridden in individual server specifications.
  • bayes_redis_prefix β€” Optional prefix for all Redis keys. Allows multiple instances to share a Redis database. Example: bayes:user1:
  • bayes_sql_dsn β€” Legacy configuration option, still supported for backward compatibility. This is a semicolon-separated list of option=value pairs, where an option can be: server, password, database. If this option is used and the new options above are not specified, the same configuration will be used for both read and write operations. Example: server=localhost:6379;password=foo;database=2
  • bayes_token_ttl β€” Controls token expiry (ttl value in SECONDS, sent as-is to Redis) when bayes_auto_expire is true. Default value is 3 weeks (but check Mail::SpamAssassin::Conf.pm to make sure).
  • bayes_seen_ttl β€” Controls 'seen' expiry (ttl value in SECONDS, sent as-is to Redis) when bayes_auto_expire is true. Default value is 8 days (but check Mail::SpamAssassin::Conf.pm to make sure).

Expiry is done internally in Redis using the *_ttl settings mentioned above, but only if bayes_auto_expire is true (which is a default). That's why --force-expire etc does nothing, and token counts and atime values are shown as zero in statistics. 😭

πŸ”„ Redis Replication Considerations πŸ”„

To maintain data consistency, this module is designed to work with Redis in a primary/replica configuration where:

  1. All write operations go to a single primary Redis server.
  2. Read operations can be distributed across multiple Redis replicas.

When setting up the Redis servers, configure:

  • One Redis server as the primary, handling all writes.
  • One or more Redis replicas (read-only) for scaling read operations.

In case of read server failure, the module will automatically attempt to connect to the next configured read server.

🏷️ Key Namespacing 🏷️

The module supports key namespacing via the bayes_redis_prefix configuration option. This allows multiple SpamAssassin instances to share the same Redis database with different key prefixes.

LIMITATIONS: Only global bayes storage is implemented, per-user bayes is not currently available. Dumping (sa-learn --backup, or --dump) of a huge database may not be possible if all keys do not fit into process memory.

✨ METHODS ✨

bestie we are IN the methods now, this is where the actual magic happens, buckle up πŸŽ€


new

public class (Mail::SpamAssassin::BayesStore::Redis) new
(Mail::Spamassassin::Plugin::Bayes $bayes)

Description: This method is literally the birth of a brand new Mail::SpamAssassin::BayesStore::Redis object, main character origin story fr. It expects you to hand it an instance of the Mail::SpamAssassin:Bayes object, which gets passed on up into the Mail::SpamAssassin::BayesStore parent object. Family tree energy 🌳

Configuration:

  • bayes_redis_read_servers β€” comma-separated list of Redis read servers, connection params included πŸ’«
  • bayes_redis_write_server β€” a single Redis write server with connection parameters ✍️
  • bayes_redis_database β€” the Redis database number (default: 0)
  • bayes_redis_password β€” Redis password for auth, keep it secret keep it safe πŸ”
  • bayes_redis_prefix β€” key prefix for namespacing (default: "")

Legacy configuration (still supported, she's vintage not expired πŸ’…):

  • bayes_sql_dsn β€” DSN-style connection string

DESTROY

Destructor method. She's done, she's leaving the function, no sequel 🫢


_connect_read

private instance (Boolean) _connect_read ()

Description: Connects to a Redis read server. It tries each configured read server one by one until one of them succeeds. If literally all of them fail, it throws an exception. picky queen behavior, and honestly? valid 😭


_connect_write

private instance (Boolean) _connect_write ()

Description: Connects to the Redis write server. Since there's only ever one write server, if the connection fails it just throws an exception. no backup plan, no plan B, she said it's this or nothing πŸ’€


_disconnect_read

private instance () _disconnect_read ()

Description: Disconnects from the Redis read server. bye bestie πŸ‘‹


_disconnect_write

private instance () _disconnect_write ()

Description: Disconnects from the Redis write server. also bye πŸ‘‹


_key

private instance (String) _key (String $key)

Description: Prefixes a key with the configured key prefix, for namespacing. it's giving "everything in its own little labeled drawer" πŸŽ€


prefork_init

public instance (Boolean) prefork_init ();

Description: This optional method is called in the parent process, shortly before it forks off child processes. pre-glow-up ritual ✨


spamd_child_init

public instance (Boolean) spamd_child_init ();

Description: This optional method is called in a child process, shortly after it gets spawned. fresh out the womb and already working 😀


tie_db_readonly

public instance (Boolean) tie_db_readonly ();

Description: This method makes sure the database connection for read operations is properly set up and working. vibe check on the read side πŸ¦‹


tie_db_writable

public instance (Boolean) tie_db_writable ()

Description: This method makes sure the database connection for write operations is properly set up and working. If it needs to, it'll initialize the database so everyone can start using it immediately. no waiting around πŸ’…


_open_db_readonly

private instance (Boolean) _open_db_readonly ()

Description: This method makes sure the database connection for read operations is properly set up and working. It also initializes the bayes variables so they can start using the database right away. ready on arrival 🫢


_open_db_writable

private instance (Boolean) _open_db_writable ()

Description: This method makes sure the database connection for write operations is properly set up and working. It will initialize the database if necessary. only if it actually has to, queen doesn't do extra πŸ’…


_check_server_info

private instance () _check_server_info ($redis)

Description: Checks and stores Redis server information. that's the version, whether Lua is available, etc. collecting lore πŸ“


untie_db

public instance () untie_db ()

Description: Closes any open db handles. You can safely call this at literally any time. no bad timing with her πŸ’–


sync_due

public instance (Boolean) sync_due ()

Description: This method figures out if a database sync is currently required.

Unused for the Redis implementation. so it's giving decorative πŸŽ€


expiry_due

public instance (Boolean) expiry_due ()

Description: This method figures out if an expire is due.

Unused for the Redis implementation. also just vibing, not on the roster 🫠


seen_get

public instance (String) seen_get (string $msgid)

Description: This method retrieves the stored value, if there is one, for $msgid. The return value is the stored string β€” 's' for spam and 'h' for ham β€” or undef if $msgid isn't found. no record? no answer. ghosted fr πŸ‘»


seen_put

public (Boolean) seen_put (string $msgid, char $flag)

Description: This method records $msgid as the type given by $flag. $flag is one of two values: 's' for spam, 'h' for ham. pick a lane bestie 🚦


seen_delete

public instance (Boolean) seen_delete (string $msgid)

Description: This method removes $msgid from the database. blocked and deleted πŸ’€


get_storage_variables

public instance (@) get_storage_variables ()

Description: This method retrieves the various administrative variables used by the Bayes process and database.

The values come back in the array in this exact order:

  1. 0: scan count base
  2. 1: number of spam
  3. 2: number of ham
  4. 3: number of tokens in db
  5. 4: last expire atime
  6. 5: oldest token in db atime
  7. 6: db version value
  8. 7: last journal sync
  9. 9: last atime delta
  10. 9: last expire reduction count
  11. 10: newest token in db atime
Heads up: only 1, 2, and 6 are actually used with Redis. Everything else returns zero, always. so jot that down πŸ“

get_running_expire_tok

public instance (String $time) get_running_expire_tok ()

Description: This method figures out whether an expire is currently running, and returns the last time that was set. is she busy? πŸ•


set_running_expire_tok

public instance (String $time) set_running_expire_tok ()

Description: This method sets the time that an expire starts running. stamping the timestamp πŸ’…


remove_running_expire_tok

public instance (Boolean) remove_running_expire_tok ()

Description: This method removes the row in the database that says an expire is currently running. she's off the clock 🫢


tok_get

public instance (Integer, Integer, Integer) tok_get (String $token)

Description: This method retrieves a specified token ($token) from the database and returns its spam_count, ham_count, and last access time. the whole trio, the full package 🎁


tok_get_all

public instance (\@) tok_get (@ $tokens)

Description: This method retrieves the specified tokens ($tokens) from storage and returns a ref to arrays of spam count, ham count, and last access time. bulk order, no extra charge πŸ“


tok_count_change

public instance (Boolean) tok_count_change (
  Integer $dspam, Integer $dham, String $token, String $newatime)

Description: This method takes a $spam_count and $ham_count and adds it to $tok, and also updates $tok's atime with $atime. numbers going up πŸ“ˆ


multi_tok_count_change

public instance (Boolean) multi_tok_count_change (
  Integer $dspam, Integer $dham, \% $tokens, String $newatime)

Description: This method takes a $dspam and $dham and adds them to all the tokens in the $tokens hash ref, and also updates each token's atime with $atime. group update, everybody gets one πŸ’–


nspam_nham_get

public instance ($spam_count, $ham_count) nspam_nham_get ()

Description: This method retrieves the total number of spam and the total number of ham that have been learned. the receipts 🧾


nspam_nham_change

public instance (Boolean) nspam_nham_change (Integer $num_spam,
                                             Integer $num_ham)

Description: This method updates the number of spam and the number of ham in the database. tallying it up ✨


tok_touch

public instance (Boolean) tok_touch (String $token,
                                     String $atime)

Description: This method updates the given token's ($token) atime.

The assumption here is that the token already exists in the database.

We will never update to an older atime. no glow-downs permitted, periodt 🚫

tok_touch_all

public instance (Boolean) tok_touch (\@ $tokens
                                     String $atime)

Description: This method does a mass update of the given list of tokens $tokens, but only if the existing token atime is < $atime. conditional queen πŸ’…


cleanup

public instance (Boolean) cleanup ()

Description: This method performs any cleanup necessary before moving on to the next operation. tidy up before the next era 🧹


get_magic_re

public instance (String) get_magic_re ()

Description: This method returns a regexp that indicates a magic token. ✨magic✨ obviously πŸͺ„


sync

public instance (Boolean) sync (\% $opts)

Description: This method performs a sync of the database. everything caught up, no pending drama 🫢


perform_upgrade

public instance (Boolean) perform_upgrade (\% $opts);

Description: Performs an upgrade of the database from one version to another. Not currently used in this implementation. she's on standby 🌸


clear_database

public instance (Boolean) clear_database ()

Description: This method deletes all records for a particular user. scorched earth, but make it user-scoped πŸ”₯

Callers should be aware that any errors returned by this method could cause the database to be inconsistent for the given user. so like, be careful bestie 😭

dump_db_toks

public instance () dump_db_toks (String $template, String $regex, Array @vars)

Description: This method loops over all tokens, computes the probability for the token, and then prints it out according to the token you passed in. data goes brrr πŸ“Š


backup_database

public instance (Boolean) backup_database ()

Description: This method will dump the user's database in a machine readable format. save your work, always πŸ’Ύ


restore_database

public instance (Boolean) restore_database (String $filename, Boolean $showdots)

Description: This method restores a database from the given filename, $filename. she's back πŸ¦‹

Callers should be aware that any errors returned by this method could cause the database to be inconsistent for the given user. same warning, same weight πŸ’œ

db_readable

public instance (Boolean) db_readable()

Description: This method returns a boolean value indicating whether the database is in a readable state. yes or no, no in-between πŸ‘‘


db_writable

public instance (Boolean) db_writable()

Description: This method returns a boolean value indicating whether the database is in a writable state. can she receive gifts or not 🎁


_define_lua_scripts

private instance () _define_lua_scripts ($redis)

Description: Defines the Lua scripts used for efficient Redis operations. Lua said "let me cook" 🍳


and that's a wrap on the methods, bestie πŸ’– every single one accounted for, no skips, no skimming, we read the whole thing like the responsible icon you are πŸ‘‘

this page came to you from perl v5.40.1, dated 2026-07-23 β€” that's the source and version for Mail::...::Redis(3pm), straight from the User Contributed Perl Documentation, so now you know where it came from, no cap πŸŽ€βœ¨

πŸ“¦ where this page comes from