https://issues.apache.org/SpamAssassin/show_bug.cgi?id=6942

            Bug ID: 6942
           Summary: Redis bayes storage module fixes and updates
           Product: Spamassassin
           Version: SVN Trunk (Latest Devel Version)
          Hardware: All
                OS: All
            Status: NEW
          Severity: normal
          Priority: P2
         Component: Plugins
          Assignee: dev@spamassassin.apache.org
          Reporter: mark.marti...@ijs.si

Spent a couple of days working on the Henrik Krohns' Redis storage backend
for Bayes (introduced by Bug 6879), fixing some of its problems and then
contributing some improvements. Testing was under Redis 2.6.13, perl 5.18.0,
on FreeBSD.

The problems first, two of them. The first was an inconsistent bookkeeping
of the $self->{is_officially_open} state, as revealed by debug logging.
The tie_db_* routines forgot to turn it on when $self->{is_really_open}
was already on. As a consequence some opportunistic calls reported
they cannot run because the backend is not open.

The second one looked quite mysterious: bayes seemed to work most of
the time, but every now and then reported either that number of spam
and ham message in the database was null, or the sub _unpack_token()
sometimes reported "bayes: unknown token format". Examining these
'unknown' tokens revealed that unrelated replies were treated as
tokens, e.g. the value of the "v:NHAM" key ended up as a value of
a token.

Turns out the cause is a re-use of cloned parent process socket by
a child process to connect to a Redis server. When several child
processes were busy at the same time, protocol got mixed up, resulting
in mixed up or missing results. When the load was light, everything
appeared to work normally.

At first I did a quick workaround, letting the module check its
process ID, comparing it to a PID under which the connection to
a Redis server was created, and re-establishing a session on a change.
This worked, but is somehow unclean, as the parent's redis sesssion
should really be closed by the master process and it should never
be propagated (through fork) to child processes.

Turns out the plugin callbacks did not offer any callback *before*
forkoff. Moreover, the existing "spamd_child_init" plugin did not
propagate through BayesStore to backend modules, so it could not
be used either.

As the opportunity of a major version bump is just right, I introduced
a new plugin callback "prefork_init", which spamd and amavisd and
mass-check now call before spawning child processes. The BayesStore
now propagates both the "prefork_init" and the "spamd_child_init"
to bayes storage modules which implement these optional methods.

The Redis backend module now closes its redis session in a master
process on "prefork_init", and (just in case) it also dismisses
existing session in "spamd_child_init", is the session happens
to still be open (e.g. some third party master process not yet
aware of the new "prefork_init" call).

While fiddling with the code, a couple of improvements were made:
- introduced new settings bayes_token_ttl and bayes_seen_ttl
  instead of repurposing bayes_expiry_max_db_size;
- avoided some unnecessary data copying;
- added a "b_learn" timing report entry;
- added some checks and debug entries which helped me debug
  the problems encountered;
- updated some comments


Seems to work very well and fast now!
A big thanks to Henrik for making this available!

-- 
You are receiving this mail because:
You are the assignee for the bug.

Reply via email to