diff --git a/CHANGELOG.md b/CHANGELOG.md index a7911643a28..fa819b4cc14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ - To prevent false positives, non-public email addresses (e.g. `user@localhost`) are no longer scrubbed by default. ([#5737](https://github.com/getsentry/relay/pull/5737)) +**Features**: + +- Envelope buffer: Add option to disable flush-to-disk on shutdown. ([#5751](https://github.com/getsentry/relay/pull/5751)) + + **Internal**: - Calculate and track accepted bytes per individual trace metric item via `TraceMetricByte` data category. ([#5744](https://github.com/getsentry/relay/pull/5744)) diff --git a/relay-config/src/config.rs b/relay-config/src/config.rs index 27e1ee8ed33..af6d19c4477 100644 --- a/relay-config/src/config.rs +++ b/relay-config/src/config.rs @@ -1024,6 +1024,14 @@ pub struct EnvelopeSpool { /// Defaults to 1. #[serde(default = "spool_envelopes_partitions")] pub partitions: NonZeroU8, + /// Whether the database defined in `path` is on an ephemeral storage disk. + /// + /// With `ephemeral: true`, Relay does not spool in-flight data to disk + /// during graceful shutdown. Instead, it attempts to process all data before it terminates. + /// + /// Defaults to `false`. + #[serde(default)] + pub ephemeral: bool, } impl Default for EnvelopeSpool { @@ -1036,6 +1044,7 @@ impl Default for EnvelopeSpool { disk_usage_refresh_frequency_ms: spool_disk_usage_refresh_frequency_ms(), max_backpressure_memory_percent: spool_max_backpressure_memory_percent(), partitions: spool_envelopes_partitions(), + ephemeral: false, } } } @@ -2347,6 +2356,11 @@ impl Config { self.values.spool.envelopes.partitions } + /// Returns `true` if the data is stored on ephemeral disks. + pub fn spool_ephemeral(&self) -> bool { + self.values.spool.envelopes.ephemeral + } + /// Returns the maximum size of an event payload in bytes. pub fn max_event_size(&self) -> usize { self.values.limits.max_event_size.as_bytes() diff --git a/relay-server/src/services/buffer/envelope_buffer/mod.rs b/relay-server/src/services/buffer/envelope_buffer/mod.rs index 087719bab0e..b5a57cebfe4 100644 --- a/relay-server/src/services/buffer/envelope_buffer/mod.rs +++ b/relay-server/src/services/buffer/envelope_buffer/mod.rs @@ -43,8 +43,8 @@ impl PolymorphicEnvelopeBuffer { /// Returns true if the implementation stores all envelopes in RAM. pub fn is_memory(&self) -> bool { match self { - PolymorphicEnvelopeBuffer::InMemory(_) => true, - PolymorphicEnvelopeBuffer::Sqlite(_) => false, + Self::InMemory(_) => true, + Self::Sqlite(_) => false, } } @@ -183,13 +183,16 @@ impl PolymorphicEnvelopeBuffer { // Currently, we want to flush the buffer only for disk, since the in memory implementation // tries to not do anything and pop as many elements as possible within the shutdown // timeout. - let Self::Sqlite(buffer) = self else { - relay_log::trace!("PolymorphicEnvelopeBuffer: shutdown procedure not needed"); - return false; - }; - buffer.flush().await; - - true + match self { + Self::Sqlite(buffer) if !buffer.stack_provider.ephemeral() => { + buffer.flush().await; + true + } + _ => { + relay_log::trace!("shutdown procedure not needed"); + false + } + } } /// Returns the partition tag for this [`PolymorphicEnvelopeBuffer`]. diff --git a/relay-server/src/services/buffer/stack_provider/sqlite.rs b/relay-server/src/services/buffer/stack_provider/sqlite.rs index b43f2ba271d..41ad2108029 100644 --- a/relay-server/src/services/buffer/stack_provider/sqlite.rs +++ b/relay-server/src/services/buffer/stack_provider/sqlite.rs @@ -19,6 +19,7 @@ pub struct SqliteStackProvider { batch_size_bytes: usize, max_disk_size: usize, partition_id: u8, + ephemeral: bool, } #[warn(dead_code)] @@ -31,9 +32,15 @@ impl SqliteStackProvider { batch_size_bytes: config.spool_envelopes_batch_size_bytes(), max_disk_size: config.spool_envelopes_max_disk_size(), partition_id, + ephemeral: config.spool_ephemeral(), }) } + /// Returns `true` if data is stored on non-persistent disks. + pub fn ephemeral(&self) -> bool { + self.ephemeral + } + /// Returns `true` when there might be data residing on disk, `false` otherwise. fn assume_data_on_disk(stack_creation_type: StackCreationType) -> bool { matches!(stack_creation_type, StackCreationType::Initialization) diff --git a/tests/integration/test_basic.py b/tests/integration/test_basic.py index 0212747b026..438d7fe101c 100644 --- a/tests/integration/test_basic.py +++ b/tests/integration/test_basic.py @@ -11,7 +11,8 @@ from requests import HTTPError -def test_graceful_shutdown_with_in_memory_buffer(mini_sentry, relay): +@pytest.mark.parametrize("backend", ["memory", "disk"]) +def test_graceful_shutdown_with_ephemeral_buffer(mini_sentry, relay, backend): from time import sleep get_project_config_original = mini_sentry.app.view_functions["get_project_config"] @@ -24,14 +25,21 @@ def get_project_config(): project_id = 42 mini_sentry.add_basic_project_config(project_id) - relay = relay( - mini_sentry, - {"limits": {"shutdown_timeout": 2}}, - ) + with tempfile.TemporaryDirectory() as db_dir: + db_file_path = ( + os.path.join(db_dir, "database.db") if backend == "disk" else None + ) + relay = relay( + mini_sentry, + { + "limits": {"shutdown_timeout": 5}, + "spool": {"envelopes": {"path": db_file_path, "ephemeral": True}}, + }, + ) - relay.send_event(project_id) + relay.send_event(project_id) - relay.shutdown(sig=signal.SIGTERM) + relay.shutdown(sig=signal.SIGTERM) # When using the memory envelope buffer, we optimistically do not do anything on shutdown, which means that the # buffer will try and pop as always as long as it can (within the shutdown timeout).