Liking cljdoc? Tell your friends :D

SSE, Buffering, Design considerations

There is some design work to do when using SSE, particularly around buffering.

When using a ring compliant adapter our SSE connection is a java.io.OutputStream. There are several considerations when dealing with those:

you may want tosolution
write bytes directlyjust use the OutputStream
write bytes with bufferinguse a java.io.BufferedOutputStream
compress the streamuse a java.util.zip.GZIPOuputStream
write textuse a java.io.OutputStreamWriter
buffer the text writesthat's where it becomes interesting for the SDK

Exploring buffering

Why buffering

  • Concatenating arrays without a buffer is really inefficient
  • 1 write operation on an OutputStream result in 1 IO syscall (at least that is the mental model).
  • With buffering we don't have a IO syscall until we explicitly flush or until the buffer is full and flushes by itself.

With limiting allocations when concatenating data, buffering is a strategy to reduce the number of IO syscalls or at least be smart about when the call is made.

SSE considerations

In the case of SSE we want to send events as they are ready, and not have them sit in a buffer.

However when creating the event's text we assemble it from parts (specific SSE lines, data lines...). We could send events line by line but this would result in 1 IO call per line.

So we need some kind of buffer to assemble an event before flushing it whole.

Here are some solutions for buffering the writes:

  1. persistent buffer: use a java.io.BufferedWriter and keep it around
  2. temporary buffer: use temporary buffer (likely a StringBuilder) that is discarded after assembling and sending 1 event
  3. buffer pooling: use some sort of buffer pooling
solutionimpact on memory
1long lived SSE -> long lived buffer -> consuming memory
2short lived buffer, consuming memory when sending only
3long lived, fix / controlable supply of buffers
solutionimpact on GC / allocations
11 allocation and done
2churning through buffers for each event sent
3controlled allocations
solutionnotes
1we can control the size of the buffer
2the jvm gc should be able recycle short lived objects
3no direct support in the jvm, gc recycling maybe be better, needs to be tuned

note

An OutputStream compression wrapper comes with an internal buffer and a context window that will both allocate and retain memory.

important

A ByteArrayOutputStream is also another buffer, it doesn't shrink in size when reset is called (see javadoc)

Datastar SDK

Considerations

There is too much ground to cover for a truly generic API. Some ring adapters are partially compliant with the ring spec and provide us with other mechanisms than an OutputStream to materialize the SSE connection. Buffer pooling isn't really part of the SDK. Going that route would mean adding a dependency to the SDK and I haven't found a ready made solution anyway.

Current SDK implementation

Common SSE machinery

starfederation.datastar.clojure.api.sse

This namespace provides 2 generic functions:

  • headers: generate HTTP headers with the SSE specific ones given a ring request.
  • write-event! provides a way to assemble an SSE event's string using a java.util.appendable.

These functions provide a basis for implementing SSE and are orthogonal to Datastar's specific SSE events.

starfederation.datastar.clojure.adapter.common

This namespace provides helpers we use to build the SSE machinery for ring adapters. It mainly provides a mechanism called "write profiles" to allow a user to configure the way the SSE connections should behave with regards to Buffering and compression.

See the write profiles doc.

Beyond the SDK

If the write profile system doesn't provide enough control there is still the possibility to implement adapters using the starfederation.datastar.clojure.protocols/SSEGenerator and control everything.

The hope is that there is enough material between the documentation and the source code to make this relatively easy.

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close