Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
fe7219d
docs(concurrent-keys): add Phase 1 design and plan
aaron-zeisler Jun 17, 2026
65fddf1
feat(credential): remove RotateWithGrace mobile-key panic (#701)
aaron-zeisler Jun 17, 2026
ecc9635
test(events): add credential routing tests for EventDispatcher (#703)
aaron-zeisler Jun 17, 2026
8eee971
test(relay): fix flaky TestAutoConfigRemovesCredentialForExpiredSDKKe…
aaron-zeisler Jun 17, 2026
8800fc2
test(sharedtest): add Phase 1 integration test utilities (#705)
aaron-zeisler Jun 17, 2026
1aa84f4
docs(concurrent-keys): incorporate T0 PoC findings and resolve Q5-Q11
aaron-zeisler Jun 17, 2026
b0214ce
docs(concurrent-keys): add T5.f (cleanup) and T5.g (merge+release) to…
aaron-zeisler Jun 17, 2026
a0b07ce
feat(credential): add Rotator accepted-set data structures (#706)
aaron-zeisler Jun 18, 2026
7f58595
fix(server): eliminate send-on-closed-channel race in StartHTTPServer…
aaron-zeisler Jun 18, 2026
cc3134b
feat(envfactory): extend EnvironmentRep and EnvironmentParams for con…
aaron-zeisler Jun 18, 2026
c8b4b6e
test(relayenv): re-anchoring PoC tests and findings (#704)
aaron-zeisler Jun 18, 2026
32724ee
docs(concurrent-keys): add end-to-end acceptance scenario catalog
aaron-zeisler Jun 22, 2026
dda0f63
test(integrationtests): increase SDK key expiry margin and big-segmen…
aaron-zeisler Jun 22, 2026
1cf3482
test(relay): fix flaky TestConcurrentKeysHarnessReference SSE race (#…
aaron-zeisler Jun 23, 2026
4585b0b
docs(concurrent-keys): add milestone view and record M2 task split
aaron-zeisler Jun 23, 2026
2d42cf8
feat(relayenv): add ReconcileCredentials API and AcceptedSet foundati…
aaron-zeisler Jun 24, 2026
d824d5d
feat(envfactory): shared BuildAcceptedSet helper for ReconcileCredent…
aaron-zeisler Jun 24, 2026
3b50d05
feat(credential): generalize cleanup ticker for per-key expiry + mobi…
aaron-zeisler Jun 24, 2026
2f36d6a
fix: Close orphaned and stale SDK clients during key rotation (#716)
keelerm84 Jun 24, 2026
8b04910
test(relayenv): verify GetClient returns anchor client in multi-key e…
aaron-zeisler Jun 25, 2026
287955a
feat(credentials): wire RAC and offline handlers to ReconcileCredenti…
aaron-zeisler Jun 26, 2026
e44881f
docs(concurrent-keys): correct T4 status-array spec to match implemen…
aaron-zeisler Jun 28, 2026
8e66e79
refactor(credential): rename sdkKey→anchor (#722)
aaron-zeisler Jun 29, 2026
991b11b
refactor(credential): remove legacy single-key rotation path from Rot…
aaron-zeisler Jun 29, 2026
ff6013e
test(concurrent-keys): integration tests for multi-key downstream aut…
aaron-zeisler Jun 29, 2026
7f3c0c4
fix(envfactory): reject primary mobile key absent from mobileKeys[] (…
aaron-zeisler Jun 30, 2026
42060b1
test: use a valid context instead of legacy user format in relay test…
aaron-zeisler Jun 30, 2026
46320e8
refactor(credential): carry wire key identifiers through the accepted…
aaron-zeisler Jun 30, 2026
46f7376
feat(credential): expose the full accepted key set via AcceptedKeys (…
aaron-zeisler Jun 30, 2026
9737404
feat(status): surface full sdkKeys[]/mobileKeys[] arrays on /status (…
aaron-zeisler Jun 30, 2026
211cec1
feat: Hand over the data store to the new client on re-anchor (#736)
keelerm84 Jul 7, 2026
e94f305
refactor: Extract rebuildEvaluator and registerCredentialMappings (#737)
keelerm84 Jul 8, 2026
3399a38
feat: Defer the SDK anchor flip to an explicit CommitAnchor (#738)
keelerm84 Jul 8, 2026
41bd432
feat: Synchronous re-anchor of the upstream SDK client (#739)
keelerm84 Jul 8, 2026
a30e36f
fix(deps): bump supported Go versions to 1.26.5 and 1.25.12 (#740)
github-actions[bot] Jul 8, 2026
62a1fa8
feat: Re-anchor big-segment sync onto the new anchor (#741)
keelerm84 Jul 16, 2026
6239d6e
feat: Build stream handlers on demand instead of per credential (#742)
keelerm84 Jul 16, 2026
bbc1599
fix: Close the demoted anchor's SDK client when a re-anchor commits (…
keelerm84 Jul 16, 2026
98086ac
refactor(relayenv): rename GetStreamHandler param to avoid shadowing …
aaron-zeisler Jul 16, 2026
e6c3afc
test(relay): fix flaky autoconfig key-rotation e2e test (#754)
aaron-zeisler Jul 17, 2026
737be47
test(concurrent-keys): additional integration tests (#755)
aaron-zeisler Jul 17, 2026
da81f6e
docs(concurrent-keys): record as-shipped re-anchor and validation sem…
aaron-zeisler Jul 20, 2026
153b78e
fix(store): prevent a startup race from leaving the environment with …
aaron-zeisler Jul 21, 2026
e96127d
refactor(concurrent-keys): remove dead code identified in final revie…
aaron-zeisler Jul 21, 2026
465caad
fix(concurrent-keys): align key expiry with the cleanup ticker and st…
aaron-zeisler Jul 21, 2026
163e410
fix(concurrent-keys): reject mobile-key payloads that arrive without …
aaron-zeisler Jul 21, 2026
4b19b77
fix(deps): bump grpc, x/net, x/text to patch disclosed CVEs (#772)
aaron-zeisler Jul 22, 2026
7efb32d
fix(deps): bump eventsource to v1.11.1 (#770)
aaron-zeisler Jul 23, 2026
9b2d3a1
test(concurrent-keys): even more integration tests (#756)
aaron-zeisler Jul 24, 2026
d98f537
test(concurrent-keys): close test coverage gaps throughout the featur…
aaron-zeisler Jul 24, 2026
a5fe46b
chore(concurrent-keys): remove project-specific details before releas…
aaron-zeisler Jul 24, 2026
c9b3a50
chore(release): set version to 8.20.0-rc.1
aaron-zeisler Jul 24, 2026
839cc52
fix(concurrent-keys): defensive hardening bundle from final review (#…
aaron-zeisler Jul 31, 2026
c0fb1b5
chore(concurrent-keys): cherry-pick outstanding v8 commits (#787)
aaron-zeisler Aug 5, 2026
dcea519
fix(concurrent-keys): reject SDK keys and mobile keys scoped to a vie…
aaron-zeisler Aug 12, 2026
4474286
test(concurrent-keys): integration coverage for view-scoped key rejec…
aaron-zeisler Aug 12, 2026
2781baa
chore(release): set version to 8.21.0-rc.1
aaron-zeisler Aug 12, 2026
718b55d
chore(release): align version.go with v8 ahead of the merge
aaron-zeisler Aug 14, 2026
31ddfba
fix(deps): bump supported Go versions to 1.26.6 and 1.25.13 (#820)
github-actions[bot] Aug 14, 2026
9d35f81
docs: Simplify comments per ASD-STE100 (#825)
keelerm84 Aug 17, 2026
f474318
test: Remove redundant tests from the concurrent-keys suite (#827)
keelerm84 Aug 17, 2026
53ad7d9
fix(autoconfigcache): bump the cached model version to 2 (#822)
aaron-zeisler Aug 17, 2026
c3cd0a9
fix(relayenv): reject revoked credentials in GetStreamHandler (#828)
aaron-zeisler Aug 18, 2026
b59a1f8
feat(projmanager): add a queue for auto-config actions per environment
aaron-zeisler Aug 18, 2026
0d84ef5
fix(autoconfig): process each environment on its own queue
aaron-zeisler Aug 18, 2026
90f9026
test(autoconfig): cover head-of-line blocking between environments
aaron-zeisler Aug 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/variables/go-versions.env
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
latest=1.26.4
penultimate=1.25.11
latest=1.26.6
penultimate=1.25.13
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# This is a standalone Dockerfile that does not depend on goreleaser building the binary
# It is NOT the version that is pushed to dockerhub
FROM golang:1.26.4-alpine3.24 as builder
FROM golang:1.26.6-alpine3.24 as builder
# See "Runtime platform versions" in CONTRIBUTING.md

RUN apk --no-cache add \
Expand Down
14 changes: 14 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ const (
// DefaultEventCapacity is the default value for EventsConfig.Capacity if not specified.
DefaultEventCapacity = 1000

// DefaultMetricsCapacity is the default for EventsConfig.MetricsCapacity: the maximum queue size
// for the usage-metrics publisher, which emits one event per unique connection on each flush.
// It exceeds DefaultEventCapacity because high-concurrency nodes exceed 1000 connections.
DefaultMetricsCapacity = 10000

// DefaultMetricsInitialCapacity is the number of events the usage-metrics publisher queue
// preallocates space for. The queue grows on demand from this size up to MetricsCapacity.
DefaultMetricsInitialCapacity = 1000

// DefaultHeartbeatInterval is the default value for MainConfig.HeartBeatInterval if not specified.
DefaultHeartbeatInterval = time.Minute * 3

Expand Down Expand Up @@ -95,6 +104,9 @@ const (
// credentials to be revoked nearly instantaneously. It is not necessarily a recommendation.
// It likely doesn't make sense to use an interval this frequent in production use-cases.
minimumCredentialCleanupInterval = 100 * time.Millisecond
// minimumMetricsCapacity is the smallest value accepted for EventsConfig.MetricsCapacity. Usage
// metrics report account usage, so the floor is the historical default of 1000.
minimumMetricsCapacity = 1000
)

// DefaultLoggers is the default logging configuration used by Relay.
Expand Down Expand Up @@ -153,6 +165,7 @@ type MainConfig struct {
GracefulShutdownTimeout ct.OptDuration `conf:"GRACEFUL_SHUTDOWN_TIMEOUT"`
HeartbeatInterval ct.OptDuration `conf:"HEARTBEAT_INTERVAL"`
MaxClientConnectionTime ct.OptDuration `conf:"MAX_CLIENT_CONNECTION_TIME"`
MaxClientRequestBodySize ct.OptBase2Bytes `conf:"MAX_CLIENT_REQUEST_BODY_SIZE"`
PingStreamJitterTime ct.OptDuration `conf:"PING_STREAM_JITTER_TIME"`
DisconnectedStatusTime ct.OptDuration `conf:"DISCONNECTED_STATUS_TIME"`
TLSEnabled bool `conf:"TLS_ENABLED"`
Expand Down Expand Up @@ -196,6 +209,7 @@ type EventsConfig struct {
SendEvents bool `conf:"USE_EVENTS"`
FlushInterval ct.OptDuration `conf:"EVENTS_FLUSH_INTERVAL"`
Capacity ct.OptIntGreaterThanZero `conf:"EVENTS_CAPACITY"`
MetricsCapacity ct.OptIntGreaterThanZero `conf:"EVENTS_METRICS_CAPACITY"`
InlineUsers bool `conf:"EVENTS_INLINE_USERS"`
MaxInboundPayloadSize ct.OptBase2Bytes `conf:"EVENTS_MAX_INBOUND_PAYLOAD_SIZE"`
}
Expand Down
28 changes: 28 additions & 0 deletions config/config_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var (
errOfflineModePropertiesWithNoFile = errors.New("must specify offline mode filename if other offline mode properties are set")
errOfflineModeWithEnvironments = errors.New("cannot configure specific environments if offline mode is enabled")
errMaxInboundPayloadSize = errors.New("max inbound payload size must be greater than zero")
errMaxClientRequestBodySize = errors.New("max client request body size must be greater than zero")
errAutoConfWithoutDBDisambig = errors.New(`when using auto-configuration with database storage, database prefix (or,` +
` if using DynamoDB, table name) must be specified and must contain "` + AutoConfigEnvironmentIDPlaceholder + `"`)
errRedisURLWithHostAndPort = errors.New("please specify Redis URL or host/port, but not both")
Expand All @@ -30,6 +31,8 @@ var (
errInvalidCredentialCleanupInterval = fmt.Errorf("expired credential cleanup interval must be >= %s", minimumCredentialCleanupInterval)
)

const warnMetricsCapacityBelowMinimum = "configured usage metrics event capacity of %d is below the minimum of %d; using %[2]d instead"

func errEnvironmentWithNoSDKKey(envName string) error {
return fmt.Errorf("SDK key is required for environment %q", envName)
}
Expand Down Expand Up @@ -85,6 +88,8 @@ func ValidateConfig(c *Config, loggers ldlog.Loggers) error {
validateOfflineMode(&result, c)
validateCredentialCleanupInterval(&result, c)
validateMaxInboundPayloadSize(&result, c)
validateMaxClientRequestBodySize(&result, c)
validateMetricsCapacity(c, loggers)

return result.GetError()
}
Expand Down Expand Up @@ -234,6 +239,29 @@ func validateMaxInboundPayloadSize(result *ct.ValidationResult, c *Config) {
}
}

func validateMaxClientRequestBodySize(result *ct.ValidationResult, c *Config) {
if c.Main.MaxClientRequestBodySize.IsDefined() {
size := c.Main.MaxClientRequestBodySize.GetOrElse(0)
if size <= 0 {
result.AddError(nil, errMaxClientRequestBodySize)
}
}
}

// validateMetricsCapacity enforces the minimum queue capacity for the usage-metrics event publisher.
// Rather than fail startup on a too-small value, it clamps the value up to the minimum and warns.
func validateMetricsCapacity(c *Config, loggers ldlog.Loggers) {
if !c.Events.MetricsCapacity.IsDefined() {
return
}
if c.Events.MetricsCapacity.GetOrElse(0) < minimumMetricsCapacity {
loggers.Warnf(warnMetricsCapacityBelowMinimum, c.Events.MetricsCapacity.GetOrElse(0), minimumMetricsCapacity)
// This value is a constant known to be greater than zero, so the constructor cannot fail.
clamped, _ := ct.NewOptIntGreaterThanZero(minimumMetricsCapacity)
c.Events.MetricsCapacity = clamped
}
}

func validateConfigDatabases(result *ct.ValidationResult, c *Config, loggers ldlog.Loggers) {
normalizeRedisConfig(result, c)

Expand Down
39 changes: 39 additions & 0 deletions config/config_validation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package config

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/launchdarkly/go-sdk-common/v3/ldlog"
"github.com/launchdarkly/go-sdk-common/v3/ldlogtest"
)

func TestValidateMetricsCapacity(t *testing.T) {
t.Run("unset value is left undefined", func(t *testing.T) {
var c Config
mockLog := ldlogtest.NewMockLog()
require.NoError(t, ValidateConfig(&c, mockLog.Loggers))
assert.False(t, c.Events.MetricsCapacity.IsDefined())
assert.Len(t, mockLog.GetOutput(ldlog.Warn), 0)
})

t.Run("value at or above the minimum is left unchanged", func(t *testing.T) {
var c Config
c.Events.MetricsCapacity = mustOptIntGreaterThanZero(2000)
mockLog := ldlogtest.NewMockLog()
require.NoError(t, ValidateConfig(&c, mockLog.Loggers))
assert.Equal(t, 2000, c.Events.MetricsCapacity.GetOrElse(0))
assert.Len(t, mockLog.GetOutput(ldlog.Warn), 0)
})

t.Run("value below the minimum is clamped up and warns", func(t *testing.T) {
var c Config
c.Events.MetricsCapacity = mustOptIntGreaterThanZero(500)
mockLog := ldlogtest.NewMockLog()
require.NoError(t, ValidateConfig(&c, mockLog.Loggers))
assert.Equal(t, minimumMetricsCapacity, c.Events.MetricsCapacity.GetOrElse(0))
mockLog.AssertMessageMatch(t, true, ldlog.Warn, "usage metrics event capacity of 500 is below the minimum of 1000")
})
}
12 changes: 12 additions & 0 deletions config/test_data_configs_invalid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,21 @@ func makeInvalidConfigs() []testDataInvalidConfig {
makeInvalidConfigDynamoDBNoPrefixOrTableName(),
makeInvalidConfigDynamoDBAutoConfNoPrefixOrTableName(),
makeInvalidConfigMultipleDatabases(),
makeInvalidConfigMaxClientRequestBodySize("0B"),
}
}

func makeInvalidConfigMaxClientRequestBodySize(size string) testDataInvalidConfig {
c := testDataInvalidConfig{name: "max client request body size " + size}
c.envVarsError = errMaxClientRequestBodySize.Error()
c.envVars = map[string]string{"MAX_CLIENT_REQUEST_BODY_SIZE": size}
c.fileContent = `
[Main]
MaxClientRequestBodySize = ` + size + `
`
return c
}

func makeInvalidConfigMissingSDKKey() testDataInvalidConfig {
c := testDataInvalidConfig{name: "environment without SDK key"}
c.fileContent = `
Expand Down
14 changes: 14 additions & 0 deletions config/test_data_configs_valid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ func mustOptIntGreaterThanZero(n int) ct.OptIntGreaterThanZero {
return o
}

func mustOptBase2Bytes(s string) ct.OptBase2Bytes {
o, err := ct.NewOptBase2BytesFromString(s)
if err != nil {
panic(err)
}
return o
}

func newOptURLAbsoluteMustBeValid(urlString string) ct.OptURLAbsolute {
o, err := ct.NewOptURLAbsoluteFromString(urlString)
if err != nil {
Expand Down Expand Up @@ -110,6 +118,7 @@ func makeValidConfigAllBaseProperties() testDataValidConfig {
IgnoreConnectionErrors: true,
HeartbeatInterval: ct.NewOptDuration(90 * time.Second),
MaxClientConnectionTime: ct.NewOptDuration(30 * time.Minute),
MaxClientRequestBodySize: mustOptBase2Bytes("5MiB"),
DisconnectedStatusTime: ct.NewOptDuration(3 * time.Minute),
TLSEnabled: true,
TLSCert: "cert",
Expand All @@ -126,6 +135,7 @@ func makeValidConfigAllBaseProperties() testDataValidConfig {
EventsURI: newOptURLAbsoluteMustBeValid("http://events"),
FlushInterval: ct.NewOptDuration(120 * time.Second),
Capacity: mustOptIntGreaterThanZero(500),
MetricsCapacity: mustOptIntGreaterThanZero(50000),
InlineUsers: true,
MaxInboundPayloadSize: ct.OptBase2Bytes{},
}
Expand Down Expand Up @@ -161,6 +171,7 @@ func makeValidConfigAllBaseProperties() testDataValidConfig {
"IGNORE_CONNECTION_ERRORS": "1",
"HEARTBEAT_INTERVAL": "90s",
"MAX_CLIENT_CONNECTION_TIME": "30m",
"MAX_CLIENT_REQUEST_BODY_SIZE": "5MiB",
"DISCONNECTED_STATUS_TIME": "3m",
"TLS_ENABLED": "1",
"TLS_CERT": "cert",
Expand All @@ -173,6 +184,7 @@ func makeValidConfigAllBaseProperties() testDataValidConfig {
"EVENTS_HOST": "http://events",
"EVENTS_FLUSH_INTERVAL": "120s",
"EVENTS_CAPACITY": "500",
"EVENTS_METRICS_CAPACITY": "50000",
"EVENTS_INLINE_USERS": "1",
"LD_ENV_earth": "earth-sdk",
"LD_MOBILE_KEY_earth": "earth-mob",
Expand Down Expand Up @@ -203,6 +215,7 @@ ExitAlways = 1
IgnoreConnectionErrors = 1
HeartbeatInterval = 90s
MaxClientConnectionTime = 30m
MaxClientRequestBodySize = "5MiB"
PingStreamJitterTime = 5m
DisconnectedStatusTime = 3m
TLSEnabled = 1
Expand All @@ -219,6 +232,7 @@ SendEvents = 1
EventsUri = "http://events"
FlushInterval = 120s
Capacity = 500
MetricsCapacity = 50000
InlineUsers = 1

[Environment "earth"]
Expand Down
5 changes: 5 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ For **Duration** settings, the value should be be an integer followed by `ms`, `
| `gracefulShutdownTimeout` | `GRACEFUL_SHUTDOWN_TIMEOUT` | Duration | `30s` | How long the Relay Proxy should wait for active connections to complete before forcefully shutting down when receiving a termination signal. This allows for graceful shutdown of the server, ensuring that in-flight requests are completed. The value should be a duration string like `30s` or `1m`. |
| `heartbeatInterval` | `HEARTBEAT_INTERVAL` | Number | `3m` | Interval for heartbeat messages to prevent read timeouts on streaming connections. Assumed to be in seconds if no unit is specified. |
| `maxClientConnectionTime` | `MAX_CLIENT_CONNECTION_TIME` | Duration | none | Maximum amount of time that Relay will allow a streaming connection from an SDK client to remain open. _(3)_ |
| `maxClientRequestBodySize` | `MAX_CLIENT_REQUEST_BODY_SIZE` | Unit | none | Maximum size of a `REPORT` request body that Relay will read when evaluating flags for a client-side, mobile, or server-side SDK. _(10)_ |
| `disconnectedStatusTime` | `DISCONNECTED_STATUS_TIME` | Duration | `1m` | How long a stream connection can be interrupted before Relay reports the status as "disconnected." _(4)_ |
| `tlsEnabled` | `TLS_ENABLED` | Boolean | `false` | Enable TLS on the Relay Proxy. Read: [Using TLS](./tls.md). |
| `tlsCert` | `TLS_CERT` | String | | Required if `tlsEnabled` is true. Path to TLS certificate file. |
Expand Down Expand Up @@ -126,11 +127,15 @@ To learn more, read [Forwarding events](./events.md).
| `eventsUri` | `EVENTS_HOST` | URI | _(7)_ | URI for the LaunchDarkly events service |
| `flushInterval` | `EVENTS_FLUSH_INTERVAL` | Duration | `5s` | Controls how long the SDK buffers events before sending them back to our server. If your server generates many events per second, we suggest decreasing the flush interval and/or increasing capacity to meet your needs. |
| `capacity` | `EVENTS_CAPACITY` | Number | `1000` | Maximum number of events to accumulate for each flush interval. |
| `metricsCapacity` | `EVENTS_METRICS_CAPACITY` | Number | `10000` | Queue capacity for the usage metrics event publisher, which reports connection usage to LaunchDarkly independently of `capacity`. See note _(9)_. |
| `inlineUsers` | `EVENTS_INLINE_USERS` | Boolean | `false` | When enabled, individual events (if full event tracking is enabled for the feature flag) will contain all non-private user attributes. |
| `maxInboundPayloadSize` | `EVENTS_MAX_INBOUND_PAYLOAD_SIZE` | Unit | _(8)_ | Maximum size of an event payload the Relay Proxy will accept from an SDK. |

_(7)_ See note _(1)_ above. The default value for `eventsUri` is `https://events.launchdarkly.com`.
_(8)_ The `maxInboundPayloadSize` setting is used to limit the size of the payload that the Relay Proxy will accept from an SDK. This is an optional safety feature to prevent the Relay Proxy from being overwhelmed by a very large payload. The default value is `0B` which provides no restriction on the payload size. The value should be a number followed by a unit: `B` for bytes, `KiB` for kibibytes, `MiB` for mebibytes, `GiB` for gibibytes, `TiB` for tebibytes, `PiB` for pebibytes, or `EiB` for exbibytes. For example, `100MiB` is 100 mebibytes.
_(9)_ The `metricsCapacity` setting controls the queue for usage metrics events, which report connection usage back to LaunchDarkly and are separate from the analytics events governed by `capacity`. The Relay Proxy emits one usage metrics event per concurrent unique connection on each flush, so this should be set to at least the number of concurrent unique connections you expect a single node to serve. This is the maximum capacity: the default is `10000` and the minimum is `1000` (smaller values are clamped up to `1000` with a warning). The queue is an in-memory buffer held per environment that starts small and grows on demand up to this maximum, so its memory footprint tracks the number of concurrent connections actually served rather than the configured maximum.

_(10)_ The optional `maxClientRequestBodySize` setting limits how much of a `REPORT` evaluation request body the Relay Proxy will read into memory before decoding the context, protecting the process from memory exhaustion caused by oversized request bodies. It applies to the `evalx` context/user endpoints for client-side, mobile, and server-side SDKs. By default it is unset, meaning there is no limit (preserving existing behavior). When set, requests whose body exceeds the limit receive an HTTP `413 Request Entity Too Large` response. The value uses the same units as `maxInboundPayloadSize` (for example, `5MiB`).


### File section: `[Environment "NAME"]`
Expand Down
Loading
Loading