Changelog

View Source

1.18.1

  • Gnat.pub/4, Gnat.request/4, and Gnat.request_multi/4 now return {:error, :max_payload_exceeded} without sending when the body plus encoded headers is larger than the server's max_payload.
    • Previously these message was sent and the server would close the connection with a Maximum Payload Violation, disrupting every subscription on it. This matches the official Go client's ErrMaxPayload. See #245
    • Gnat.Server and Gnat.Services.Server now pass :max_payload_exceeded to your error/2 callback when a reply is rejected. If the reply returned from error/2 is also rejected, it is logged instead of calling error/2 again. Services count a rejected reply as an endpoint error instead of a successful request.
    • JetStream APIs built on Gnat.request/4 (KV, Object store, and so on) can now return {:error, :max_payload_exceeded}.
  • Fixed subscription cleanup when a subscriber process exits. See #244
    • A subscriber exit that no longer matched a subscription used to crash the connection. It's now ignored.
    • A process with several subscriptions could leave one behind when it exited. Each subscription is now matched by its own monitor.
    • A subscription with Gnat.unsub/3's max_messages option is now removed, and an UNSUB is sent, if the subscriber exits before the last message. Previously the subscription leaked.

1.18.0

  • Supervised PullConsumers will now only attempt to re-create the consumer on disconnect/reconnect for ephemeral consumers. Any durable consumer will just register a new pull request for its durable name. See #233 for details.
  • Subject names with \r, \n or \t characters are now validated by the client before sending over the wire to prevent potential injection. See #232
  • Behavior change: Gnat.start_link/2 now returns {:error, reason} when the TLS upgrade, the header support check or sending CONNECT fails, instead of crashing during initialization. TLS alerts come back as-is (for example {:error, {:tls_alert, {:certificate_expired, _}}}), so Gnat.ConnectionSupervisor logs why the connection failed. Invalid ssl_opts return {:error, {:options, {option, :redacted}}} because :ssl echoes the option value, which can be a key or password.
  • Behavior change: Gnat.Jetstream.API.KV.put_value/5, delete_key/4, and purge_key/4 now validate the JetStream publish acknowledgement instead of reporting :ok for any reply. They share the same ack validation as create_key/5: a rejected write returns {:error, reason} with the server's error map, and a malformed or unrelated ack returns {:error, :invalid_publish_ack}. Previously a server-side rejection (for example a put_value/5 larger than the bucket's max_value_size) was silently reported as :ok. See #235
  • Behavior change: Gnat.Jetstream.API.KV.create_key/5 now only creates a key when one doesn't already exist. Previously it behaved like put_value/5 and silently overwrote existing keys, and it returned :ok even when the server rejected the write. This aligns the library with the NATS server and the official clients: the publish is conditional on the key's current revision (Nats-Expected-Last-Subject-Sequence), so concurrent creators can't overwrite each other, and keys that were deleted or purged can be recreated. If you relied on create_key overwriting an existing key, use put_value/5 instead. Thanks to @ericmj for #234
    • Returns {:error, :key_exists} when a live value already exists for the key.
    • Validates the publish acknowledgement and returns {:error, reason} when the server rejects the write.
    • Accepts a :domain option so the tombstone lookup works on JetStream domains.
  • Gnat.Jetstream.API.KV.Entry.operation/1 is now public and is the single definition of how KV records are classified as a put, delete, or purge. It follows the official clients: a kv-operation header takes precedence, and server-generated nats-marker-reason markers map MaxAge and Purge to :purge and Remove to :delete. create_key/5, keys/3, contents/3, and the watcher all share it.
    • Behavior change: the watcher now reports keys removed by a TTL limit marker as :key_purged instead of :key_deleted.
    • keys/3 and contents/3 now exclude keys removed by limit markers, and contents/3 no longer includes purged keys with an empty value.

1.17.0

  • Adds support for PullConsumer with ack_policy: :explicit that pull in batch mode. This makes durable and multi-client consumers strongly consistent. Big thanks to @ericmj for #230
    • Also fixes a bug where batch consumers with ack_policy: :all used to ignore return values of :nack, :term and :noreply which was confusing/incorrect (we logged an error and treated them as :ack). Now we will raise an ArgumentError and require that your PullConsumers with ack_policy: :all can only respond with :ack since we can't individually address the messages with our flow.
  • Gnat.Jetstream.API.Object now validates that each chunk properly stored in the stream and does content hash validation on read. You can now rely on getting :ok vs :error from this API. Previously we didn't check the SHA-256 hash on read, so you could get :ok even if we missed a chunk or the final contents didn't line up with the recorded integrity hash. Again, thanks to @ericmj for #231

1.16.1

  • Fix PullConsumer dropping :request_expires and :idle_heartbeat after successfully processing a message. The resulting unbounded, heartbeat-free pull caused false watchdog reconnects on quiet streams. ACK-next requests now retain the configured pull options.

1.16

  • Add support for the name connection setting, which is reported to NATS monitoring endpoints. Contributed by @ahamez in #227
  • Require Elixir 1.16+. Elixir 1.14 and 1.15 are no longer supported (both are past their upstream maintenance window). The CI matrix now runs on Elixir 1.16 through 1.19 and Erlang/OTP 26 through 28.

1.15

  • PullConsumer now requests an idle_heartbeat (defaults to half of :request_expires, i.e. 2.5s) from the server on every long-poll pull request and runs a local watchdog. If no traffic (data, status, or 100 heartbeat) is observed within 2 * idle_heartbeat, the consumer assumes its pull request was lost (e.g. dropped during a JetStream leadership change without killing the TCP connection) and forces a reconnect. Configurable via the :idle_heartbeat and :heartbeat_check_interval options.
  • PullConsumer emits a new telemetry event [:gnat, :jetstream, :pull_consumer, :heartbeat_expired] when the watchdog fires. Measurements: %{gap_ms, threshold_ms}. Metadata: %{module, stream_name, consumer_name, connection_name}.
  • Improve parsing performance for large messages. Contributed by @cbecker in #225
  • Remove the cowlib dependency, which carried an unfixed CVE flagged by mix hex.audit. Header encoding/decoding is now handled internally with no change in behavior. Contributed by @tank-bohr in #226

1.14

  • Add PullConsumer.handle_connected/2 optional callback to get consumer info
  • Add PullConsumer.handle_status/2 optional callback to observe status messages
  • Added support for batch_size option in PullConsumer options to pull messages and acknowledge them in batches.
  • Add Gnat.Jetstream.API.KV.Entry with from_message/2 for parsing a raw NATS message from a KV bucket's underlying stream into a structured entry (operation, key, value, revision, created, delta). Intended to be shared between the built-in KV.Watcher and user-supplied PullConsumer implementations (e.g. caches that need to detect when they are caught up with the stream). Returns :ignore for messages that are not KV records.
  • KV.Watcher now uses KV.Entry internally; its public callback API is unchanged. The push consumer it creates now enables server-driven flow control and a 5s idle heartbeat (matching nats.go's ordered-consumer defaults), so slow handlers apply backpressure instead of being dropped as slow consumers.
  • Behavior change (bugfix): PullConsumer no longer forwards JetStream informational status messages (e.g. 100 idle heartbeat, 409 leadership change) to c:handle_message/2. These are not stream records and cannot be acked. In single-message mode the consumer now drops them and re-issues a pull request.
  • Add an optional c:handle_status/2 callback to Gnat.Jetstream.PullConsumer for users who want to observe status messages (e.g. log on 409).

1.11

  • Allow clients to force authentication without server auth_required by @mmmries in #205
  • Support ephemeral consumers and auto-cleanup consumers in the PullConsumer module by @mmmries in #202
  • Implement regularly scheduled PING/PONG health check by @mmmries in #200
  • Add Erlang 28 with Elixir 1.18 to the build matrix by @davydog187 in #201
  • Use Pager module for KV.contents by @mmmries in #198
  • Hint :timeout option in KV's typespec by @rixmann in #193

1.10

  • Clarify authentication setup during test by @davydog187 in #187
  • Test on Elixir 1.18 and NATS 2.10.24 by @davydog187 in #188
  • Gnat.Jetstream.API.KV.info/3 by @davydog187 in #189
  • Remove function_exported? check for Keyword.validate!/2 by @davydog187 in #186
  • Tiny optimization to KV.list_buckets/1 by @davydog187 in #185
  • make KV-watcher emit :key_added events when the message has a header by @rixmann in #191
  • add :compression to stream attributes by @rixmann in #192
  • fix: unknown field domain in Stream.create (#194) by @c0deaddict
  • feat: add jetstream message metadata helper (#197) by @c0deaddict
  • fix: deliver policy (#196) by @c0deaddict

1.9

  • Housecleaning by @mmmries in #176
    • switch to charlist sigils
    • update to newest nkeys
    • require elixir 1.14 and erlang 25+
  • Fix incorrect useage of charlist by @davydog187 in #179
  • Soft deprecate is_kv_bucket_stream?/1 in favor of kv_bucket_stream?/1 by @davydog187 in #183
  • Clean up examples in KV by @davydog187 in #181
  • Document options for Gnat.Jetstream.API.KV by @davydog187 in #180

1.8

1.7

1.6

1.5

1.4

1.3

1.2

1.1

1.0

0.7.0

0.6.0

  • Dropped support for Erlang < 19 and Elixir <= 1.5
  • Added Telemetry to the project (thanks @rubysolo)
  • Switched to nimble_parsec for parsing
    • Updated benchmarking/performance information. We can now do 170k requests per second on a 16-core server.
  • Fixed a bug around re-subscribing for the ConsumerSupervisor
  • Pass sid when delivering message (thanks @entone)
  • Documentation fixes from @deini and @johannestroeger

0.5.0

  • Dropped support for Elixir 1.4 and OTP 18 releases. You will need to use Elixir 1.5+ and OTP 19+.
  • Switched to running our tests against gnatsd 1.3.0