Avoid blocking incoming messages.

Through the additional of a secondary signalling channel,
whenever there are sufficient messages in the queue to fill a "batch",
the processing goroutine will be immediately woken. Because the
buffered channel has double that capacity, requests should never be
delayed unless the server is actually overloaded.

This preserves the improvements of the previous commit, meaning that,
even when a batch size of 10 and a batch delay of 1s is used:
- there is no background load; if no messages are received, the server
  is dormant;
- if no message was received in the last second, a new message will be
  immediately processed;
- if a small number of messages are received after the first, the
  additional messages will be collected and processed in a single
  transaction after the configured delay;
- if enough new messages arrive to fill a batch (ie 10 in this example),
  they will be immediately processed, freeing up capacity for more
  messages;
- if up to double the configured number of messages arrive in a burst,
  there will be sufficient capacity to cache them immediately,
  regardless of how slowly the mysql server commits each transaction
This commit is contained in:
Nick Farrell 2022-12-17 17:26:33 +11:00
parent 09e8fb81b5
commit 9f2311b98e
No known key found for this signature in database
GPG key ID: 740D3A86CF435835
2 changed files with 28 additions and 20 deletions

View file

@ -76,18 +76,14 @@ func TestBufferedCacheFlushBehaviour(t *testing.T) {
require.Nil(t, err)
require.Equal(t, 2, counts["mytopic"])
// Add an extra message. Because the buffered queue is at capacity, this should block
// this goroutine until the cooldown period has expired, and at least one of the pending
// messages has been read from the channel.
// Add an extra message.
require.Nil(t, c.AddMessage(newDefaultMessage("mytopic", "my example message")))
require.Greater(t, time.Since(t1), cooldown/3)
// Because the channel was full, there should not be a cooldown, and our new message should
// be processed without delay
time.Sleep(cooldown / 3)
// Because we have more than `queueSize` elements in the channel, the processing
// goroutine should be immediately woken, processing everything very quickly.
time.Sleep(cooldown / 6)
counts, err = c.MessageCounts()
require.Nil(t, err)
require.Equal(t, 3+queueSize, counts["mytopic"])
require.GreaterOrEqual(t, 2+queueSize, counts["mytopic"])
}
func testCacheMessages(t *testing.T, c *messageCache) {