Commit Graph
120 Commits
Author SHA1 Message Date
Wez Furlong 3fc64011eb message: add save_and_shrink_data method
We don't use this anywhere yet
2025-02-21 09:17:27 -07:00
Wez Furlong 45d8449c69 message: add msg:shrink() and msg:shrink_data() methods
These are helpful when explicitly managing memory overheads
2025-02-19 15:55:12 -07:00
Wez Furlong d331df5965 dkim: add separate key cache to signer machinery
The signer cache maps the signer parameters to a pre-made
signing context.

It is essentially a map of (domain, key) -> signer.

Assuming that the (domain,key) tuple is unique, then this is
a good, effective use of that cache.

However, if multiple domains can share the same key then we
can end up re-parsing the same key data for each of them,
which is moderately expensive and a waste of CPU.

This commit introduces an additional cache for the key source to
the resultant compiled key.

This allows sharing of the same compiled key across signing
parameters that otherwise vary, and should help to shave off
some latency.
2025-02-06 16:47:15 -07:00
Wez Furlong 832121e337 dkim: avoid creating unused threads for SIGN_POOL
We only use the blocking threads portion of the thread pool,
so avoid the default of creating number-of-cores threads
for the IO portion.  We have to create one even though we
don't use it, otherwise tokio is unhappy.
2025-02-06 16:47:14 -07:00
Wez Furlong 3d22151d14 add SingletonTimerWheelV2 strategy option
This uses our new leaner timeq data structure; that has lower
memory overhead and is a bit more efficient at ticking.

It is not the default at this time, but ideally will become
the default in the future once it has been proven out.
2025-02-06 13:38:06 -07:00
Wez Furlong 7a12d37b5b roll our own timeq
This one uses our intrusive list so that the memory overhead is
constant, and allows us to iterate the contents, retain() style.
2025-02-06 12:38:08 -07:00
Wez Furlong cf09289579 dkim: allow duration strings to be used for signer ttls
This is a nice convenience
2025-02-05 11:22:46 -07:00
Wez Furlong 41b6e79c4e tidy up chrono timedelta math
I'm investigating an issue where a block of messages appear to be stuck
in a scheduled state.  In looking at the math here, I'm wondering if it
is possible for some overflow to happen when converting from chrono
to std time types, so adjust the logic to be less prone to such a thing,
if it is occurring.
2025-01-24 15:11:03 -07:00
Wez Furlong cf6f3540e7 Add new Delay log record type
The purpose of this record is to log additional context about why
a message might end up in the scheduled queue when it hasn't
logged a TransientFailure.

There are a few situations around handling throttles and limits
where we might put a message back into the scheduled queue, without also
logging a TransientFailure record. It's possible that we should
reconsider some of those, but for the moment, there is an observability
hole that needs to be filled.

What this commit does is introduce an `InsertContext` which can hold one
or more `InsertReason`s about why a message is being inserted into the
scheduled queue.

There are 3 primary reasons for insertion:

* Received - the message was just received/injected
* Enumerated - the message was discovered in spool enumeration
* DueTimeWasReached - the message is now due for delivery and is being
  popped off the scheduled queue

The additional reasons can be added to the context to provide more
color about what happened.

When a message is added to the scheduled queue, the accumulation
in the InsertContext is examined, and if the context doesn't
indicate that the message was Enumerated and it wasn't also
already logged as a TransientFailure, a `Delay` record is
logged.

The `Delay` record includes in its `response.content` the ordered set of
InsertReasons as well as the delay duration and due time.

Logging Delay records might place undesirable pressure on the
logging storage, so you may wish to disable it via:

```lua
kumo.configure_local_logs {
  per_record = {
    Delay = {
      -- Suppress Delay records
      enable = false
    }
  }
}
```

or similar.
2025-01-17 13:21:47 -07:00
Wez Furlong 6efff15983 rfc5321 parser: allow utf8 in mailboxes
This is a step towards supporting SMTPUTF8.

This commit allows MAIL FROM and RCPT TO to contain UTF8.

UTF8 in the domain portion is converted to an A-label form
via the IDNA crate, raising an error if it is invalid.

UTF8 in the local part is unchanged.

The SMTP server now explicitly checks to see if the provided
MAIL FROM or RCPT TO is non-ascii, and rejects it; we won't
try to relay UTF8 local parts as a result of this change,
but will now accept UTF8 domain parts and relay, because we
rewrite those via IDNA.

refs: https://github.com/KumoCorp/kumomta/issues/327
2025-01-11 06:53:43 -07:00
Wez Furlong 68c3606420 ready_queue: use MessageList when taking ready queue
Now that we have doubly-linked-lists of Messages for these things,
it is more efficient to take the whole list when we're doing
bulk ready queue operations, so let's do that.
2024-12-10 17:55:12 -07:00
Wez Furlong c13dde9ecb message: introduce intrusive MessageList type, replace Fifo internals
I've been recently troubleshooting a couple of systems with high memory
usage, and in one of them there were very large amounts of
memory being allocated to ready queues.  That could be partially
mitigated by reducing `max_ready` to a more reasonable and small value,
but it is difficult to compute the right balance between large-enough
for high throughput and small-enough to keep memory usage reasonable.

This commit switches away from crossbeam's ArrayQueue, which
pre-allocates sufficient space to hold exactly `max_ready` messages for
each instantiated ready queue, and to a newly introduced MessageList,
which is an intrusive doubly-linked list.

The intrusive list, in exchange for some small additional overhead
per-Message, requires no auxilliary additional memory allocations to
track the membership of that Message in some other list.

That means that `max_ready` is no longer a pre-allocated minimum amount
of additional storage, and changes the memory overhead from
`O(number-of-queues * max_ready)` to `O(number-of-ready-messages)`,
which is typically a lot smaller.  This is independent of the individual
messages metadata and bodies that are nominally associated with being in
a ready queue.
2024-12-10 14:29:41 -07:00
Wez Furlong 62672a2fbd deps: upgrade mlua to 0.10
This is a reasonably significant upgrade, as it allows for
async functions to be Send, which could unlock more efficiencies
in our overall scheduling.

This commit is just the basic changes required to get the updated
mlua version compiling, it doesn't change anything else.
2024-12-07 09:17:54 -07:00
Dirkjan Ochtman eead204a2f Remove unused dependencies 2024-11-13 13:50:27 -07:00
Dirkjan Ochtman 18db31f204 Manage all external dependencies at the workspace level 2024-11-13 13:50:27 -07:00
Wez Furlong 7a9a33f836 apply cache-purge-on-low-mem to all LruCaches 2024-11-06 20:50:30 -07:00
Dirkjan Ochtman 197b73330b dkim: migrate to dns-resolver Resolver trait 2024-10-17 09:46:59 -07:00
Dirkjan Ochtman a28721f5ee dns-resolver: replace Resolver enum with a trait 2024-10-15 08:54:24 -07:00
Dirkjan Ochtman e309c8d69a dns-resolver: avoid exposing internal structure 2024-10-15 08:54:24 -07:00
Dirkjan Ochtman 6c37410042 Use a workspace dependency for serde 2024-10-15 06:04:53 -07:00
Wez Furlong 379d8aebbd fixup shrinking during low memory
Two issues here:

1. The maintainer would repeatedly try to shrink the ready queue
   each time it woke up during a low memory condition.  This has
   been fixed to trigger whenever we transition to low memory
   instead.

2. At some point during refactoring, we dropped the save part
   of the message shrinking processing, so we'd only successfully
   shrink messages that had previously been saved and stored to
   spool.  Messages that we deferred spooling, or otherwise modified
   post reception, would not be saved and thus not be eligible to
   shrink.

Ideally we'd have an integration test for this, but it is a bit awkward
because we'd need to contrive an appropriate ulimit for just this
instance and generate appropriate load to trip over that limit.  I don't
fancy the chances that such a test wouldn't be flakey.

My ad-hoc test procedure was:

ulimit -m 2028527
./target/release/kumod  --policy simple_policy.lua

then in another window:

./target/release/traffic-gen --target 127.0.0.1:2025 --body-size 100000 --duration 3600 --throttle 15000/s --http

the generator will eventially experience load shedding http responses,
and you can see the shrink procedure triggering in the kumod output.

curl -s 'http://127.0.0.1:8000/metrics' | grep memory

can also be used to check the usage, limit and how many times it trips.

You can also use smtp for this, but the smtp client in traffic gen will
try hard to reconnect without telling you about the shortage, so you
will need to look at the metrics to see it happening.
2024-10-02 14:49:28 -07:00
Wez Furlong f4390fc95a dkim signing: add preliminary support for dkim signing pool
We've been examining a system using http injection where it
appears as though dkim signing might benefit from being broken
out into a dedicated cpu-bound thread pool.

This commit is a fairly quick prototype to allow us to explore
that.

It isn't quite what I'd consider to be the end state for this,
because the pool doesn't export metrics like the others do.
That is because this is a blocking pool rather than an async
pool.

Usage is to define the pool in pre_init:

```
 kumo.on('pre_init', function()
   kumo.dkim.set_signing_threads(24)
 end)
```

If you don't define a pool, then the signing happens
on the calling thread, just as in prior versions.
2024-09-25 12:24:04 -07:00
Dirkjan Ochtman 4df3bfd471 Replace lazy-static with LazyLock 2024-09-24 06:31:21 -07:00
Wez Furlong 7ec15e9b09 message: refactor: split QueueNameComponents into separate module
No functional change
2024-09-12 13:34:36 -07:00
Wez Furlong 0f153774f3 message: reduce MessageInner size from 144 to 32 bytes
A message in the scheduled queue will thus occupy 32 bytes (with
metadata unloaded), plus 8 bytes per Message reference. For TimerWheel
and SkipList queues, that's just 8 bytes. For SingletonTimerWheel it is
2*8 bytes (one for the queue and one for the singleton wheel).  The
singleton usage is overall lower though, because the wheel overheads are
reduced to a single wheel in that mode.
2024-09-02 10:42:14 -07:00
Wez Furlong 3d4a5d2194 mailparsing: relax Content-ID and Message-ID parsing
This is an area where conformance is inconsistently applied by
various clients, so we're relaxing our parser to try the current
strict rules first, which can normalize stuff like `<"foo"@id>`,
but then allow falling back to anything between `<>`.

We will still blow up for wonky stuff like `<<wat>>`, as proven
by our existing test coverage.

closes: #259
2024-08-26 08:11:20 -07:00
Wez Furlong a7cddd64bb message: allowing hashing and taking a weak reference 2024-08-24 17:31:28 -07:00
Wez Furlong ceb54eac6c message: add cache hit/miss/lookup counters for dkim signer 2024-08-22 11:07:08 -07:00
Wez Furlong 6fcb8ca47e add dkim signing histograms to metrics 2024-08-20 10:03:26 -07:00
Wez Furlong 282c67171e message: save 16 bytes per Message handle
Adjust how we track the data in Message to make it a little smaller
and cheaper to maintain multiple clones.

Previously, to avoid locking overhead during debug or other
introspection, we kept the spoolid (a 16-byte uuid) outside of the
Arc<Mutex<Inner>>.  This has the consequence of making a fatter
handle type and there are a number of places (notably, every
time we pass the message into lua) where we might clone the
handle.

This commit introduces another intermediate type so that we can make
Message effectively equivalent to an Arc (size == pointer size)
and then the intermediate type has the spoolid outside of the mutex,
with the mutex protected data alongside it.

This changes the size of Message from 16 + 8 to just 8.
For a single Message handle we don't really save anything overall,
because the same amount of data needs to exist, but for each subsequent
clone we save 16 bytes.
2024-08-19 19:53:08 -07:00
Wez Furlong 9aea77a80b timeq: remove cancellation functionality
We weren't using this outside of the unit tests, and it introduces
additional memory overheads and a layer of indirection that we
can do without, so let's remove it.
2024-08-15 18:32:52 -07:00
Wez Furlong d4ccf76c3a message: add latency histograms for saving and meta/data loading 2024-08-15 15:16:38 -07:00
Wez Furlong c524ef5d11 deps: upgrade mlua from 0.8 -> 0.9
I've been holding off on this for a while, because I remember
this upgrade being a bit of a PITA for wezterm.  Since then
there is a `derive(FromLua)` that makes the transition much
easier.

This upgrade makes it potentially a bit easier/nicer/cheaper for
memoize, because it is now possible to retrieve references to userdata
values (rather than always cloning them), but I haven't done anything
special to take advantage of that in this commit.
2024-06-28 17:28:42 -07:00
Wez Furlong a2d76df7a9 NEW: rebind API and kcli subcommand
refs: https://github.com/KumoCorp/kumomta/issues/209
2024-06-24 12:26:06 -07:00
Wez Furlong 626a7608e4 Fix bad message-id breaking message rebuild
The issue only triggered when there were issues that required
rebuilding the message body in addition to the bad header.

The fix here is to silently ignore any header parsing errors
during a rebuild.

closes: #216
2024-06-17 13:50:53 -07:00
Wez Furlong a1fae3a634 rocksdb: improve write/delete throughput
This adjusts how we do write and delete operations so that we
first try to immediately submit the operation to the WAL,
but if that would block, then we push the operation to
the tokio blocking thread pool in order not to hard-block
the tokio schedule thread when rocksdb needs to perform writes.

While in there, hook up the force-sync flag to the closest
approximation in the WriteOptions for the batch (of size 1).
2024-05-03 20:03:49 -07:00
Wez Furlong e81a5fb6ca fix: msg:set_scheduling didn't immediately recompute due time 2024-04-09 14:34:38 -07:00
Wez Furlong 4cbe681d8d msg: remove stray debug prints from scheduling logic 2024-04-09 14:32:30 -07:00
Wez Furlong 898ed52410 add msg:append_text_html and msg:append_text_html
These can be used to insert tracking pixels/links.

closes: https://github.com/KumoCorp/kumomta/issues/120
2024-03-31 14:04:46 -07:00
Wez Furlong 1fb712e2bf Add msg:set_data()
This method allows replacing the message payload arbitrarily.
This is a building block for more advanced and convenient
modification operations.

refs: https://github.com/KumoCorp/kumomta/issues/117
refs: https://github.com/KumoCorp/kumomta/issues/120
2024-03-31 06:57:59 -07:00
Wez Furlong 0dd1cd0731 deps: update k9 2024-03-28 06:56:10 -07:00
Wez Furlong 78e062ad64 chrono: fixup now-deprecated calls 2024-03-11 07:50:01 -07:00
Wez Furlong 33c7557afb msg: add num_attempts, set_due and queue_name methods
refs: https://github.com/KumoCorp/kumomta/issues/149
2024-03-08 13:35:03 -07:00
Wez Furlong 81a79ace76 add more context to "Data is not ASCII or UTF-8" errors
We've had a report that `msg:from_header().domain` is failing
on an input in an unexpected way with this error message,
so let's try to get a bit more context on what is happening.
2024-02-27 06:46:29 -07:00
Wez Furlong 334a19963c deps: base64 -> data_encoding
dependabot wanted to upgrade to base64 0.21, but I resent that
the API for the common case of base64 encoding got more difficult
in the move from 0.13.

So, I'm switching us to data_encoding's implementation instead.

Note that the overall rust ecosystem on which we depend here,
transitively depends on 3 different versions of the base64 crate, so
even though we've removed direct deps on base64 from kumomta, the lock
file still references base64 0.10, 0.13 and 0.21, so that kinda sucks.
2024-02-06 13:56:41 -07:00
ncai c3c5e34067 add default to dkim over_sign 2024-01-26 05:59:11 -07:00
Wez Furlong 02cd1ef7bf NEW: dkim over signing by setting over_sign = true in the signer
refs: https://github.com/KumoCorp/kumomta/issues/111
2024-01-18 12:59:41 -07:00
Wez Furlong e6973c2a04 slim down some deps
This helps to remove "false" deps on things like openssl; now that
is linked in only to those components that need it (eg: things that
use unbound for dnssec, or the smtp client for DANE).

This makes the binaries other than kumod (which still has max deps)
a few MB smaller; not a massive win, but nice all the same.
2023-12-22 10:24:30 -07:00
Wez Furlong e9ec19fd23 fix an issue where msg:[sg]et_meta would not round-trip nil values 2023-12-13 14:21:12 -07:00
Wez Furlong 06bb8d0492 add msg:add_authentication_results
This method will prepend an Authentication-Results header
to the message with the specified set of results and
local server name.

We need a more convenient way to default the server name;
ideally we'd pick this up from the listener, but that is currently
awkward in the policy.

refs: https://github.com/KumoCorp/kumomta/issues/82
2023-09-14 13:03:41 -07:00