diff --git a/dev-tools/omdb/src/bin/omdb/nexus.rs b/dev-tools/omdb/src/bin/omdb/nexus.rs index 6413eed5069..00430e884a0 100644 --- a/dev-tools/omdb/src/bin/omdb/nexus.rs +++ b/dev-tools/omdb/src/bin/omdb/nexus.rs @@ -82,6 +82,7 @@ use nexus_types::internal_api::background::SupportBundleCleanupReport; use nexus_types::internal_api::background::SupportBundleCollectionReport; use nexus_types::internal_api::background::SupportBundleCollectionStepStatus; use nexus_types::internal_api::background::SupportBundleEreportStatus; +use nexus_types::internal_api::background::TokenCleanupStatus; use nexus_types::internal_api::background::TrustQuorumManagerStatus; use nexus_types::internal_api::background::TufArtifactReplicationCounters; use nexus_types::internal_api::background::TufArtifactReplicationRequest; @@ -1340,6 +1341,9 @@ fn print_task_details(bgtask: &BackgroundTask, details: &serde_json::Value) { "session_cleanup" => { print_task_session_cleanup(details); } + "token_cleanup" => { + print_task_token_cleanup(details); + } "sp_ereport_ingester" => { print_task_sp_ereport_ingester(details); } @@ -2813,6 +2817,33 @@ fn print_task_session_cleanup(details: &serde_json::Value) { }; } +fn print_task_token_cleanup(details: &serde_json::Value) { + match serde_json::from_value::(details.clone()) { + Err(error) => eprintln!( + "warning: failed to interpret task details: {:?}: {:?}", + error, details + ), + Ok(status) => { + const DELETED: &str = "deleted:"; + const CUTOFF: &str = "cutoff:"; + const LIMIT: &str = "limit:"; + const ERROR: &str = "error:"; + const WIDTH: usize = + const_max_len(&[DELETED, CUTOFF, LIMIT, ERROR]) + 1; + + println!(" {DELETED:(details.clone()) { Err(error) => eprintln!( diff --git a/dev-tools/omdb/tests/env.out b/dev-tools/omdb/tests/env.out index f6457634107..aaa69db7b83 100644 --- a/dev-tools/omdb/tests/env.out +++ b/dev-tools/omdb/tests/env.out @@ -242,6 +242,10 @@ task: "switch_port_config_manager" manages switch port settings for rack switches +task: "token_cleanup" + hard-deletes expired device access tokens + + task: "trust_quorum_manager" Drive trust quorum reconfigurations to completion @@ -505,6 +509,10 @@ task: "switch_port_config_manager" manages switch port settings for rack switches +task: "token_cleanup" + hard-deletes expired device access tokens + + task: "trust_quorum_manager" Drive trust quorum reconfigurations to completion @@ -755,6 +763,10 @@ task: "switch_port_config_manager" manages switch port settings for rack switches +task: "token_cleanup" + hard-deletes expired device access tokens + + task: "trust_quorum_manager" Drive trust quorum reconfigurations to completion diff --git a/dev-tools/omdb/tests/successes.out b/dev-tools/omdb/tests/successes.out index da2195647c7..66ca4669072 100644 --- a/dev-tools/omdb/tests/successes.out +++ b/dev-tools/omdb/tests/successes.out @@ -477,6 +477,10 @@ task: "switch_port_config_manager" manages switch port settings for rack switches +task: "token_cleanup" + hard-deletes expired device access tokens + + task: "trust_quorum_manager" Drive trust quorum reconfigurations to completion @@ -996,6 +1000,14 @@ task: "switch_port_config_manager" started at (s ago) and ran for ms warning: unknown background task: "switch_port_config_manager" (don't know how to interpret details: Object {}) +task: "token_cleanup" + configured period: every m + last completed activation: , triggered by + started at (s ago) and ran for ms + deleted: 0 + cutoff: + limit: 10000 + task: "trust_quorum_manager" configured period: every m last completed activation: , triggered by @@ -1679,6 +1691,14 @@ task: "switch_port_config_manager" started at (s ago) and ran for ms warning: unknown background task: "switch_port_config_manager" (don't know how to interpret details: Object {}) +task: "token_cleanup" + configured period: every m + last completed activation: , triggered by + started at (s ago) and ran for ms + deleted: 0 + cutoff: + limit: 10000 + task: "trust_quorum_manager" configured period: every m last completed activation: , triggered by diff --git a/nexus-config/src/nexus_config.rs b/nexus-config/src/nexus_config.rs index 9f035510bc9..1cce52998ed 100644 --- a/nexus-config/src/nexus_config.rs +++ b/nexus-config/src/nexus_config.rs @@ -438,6 +438,8 @@ pub struct BackgroundTaskConfig { pub attached_subnet_manager: AttachedSubnetManagerConfig, /// configuration for console session cleanup task pub session_cleanup: SessionCleanupConfig, + /// configuration for device access token cleanup task + pub token_cleanup: TokenCleanupConfig, /// configuration for audit log incomplete timeout task pub audit_log_timeout_incomplete: AuditLogTimeoutIncompleteConfig, /// configuration for audit log cleanup (retention) task @@ -455,6 +457,17 @@ pub struct SessionCleanupConfig { pub max_delete_per_activation: u32, } +#[serde_as] +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct TokenCleanupConfig { + /// period (in seconds) for periodic activations of the token cleanup task + #[serde_as(as = "DurationSeconds")] + pub period_secs: Duration, + + /// maximum rows hard-deleted per activation + pub max_delete_per_activation: u32, +} + #[serde_as] #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct AuditLogTimeoutIncompleteConfig { @@ -1333,6 +1346,8 @@ mod test { attached_subnet_manager.period_secs = 60 session_cleanup.period_secs = 300 session_cleanup.max_delete_per_activation = 10000 + token_cleanup.period_secs = 300 + token_cleanup.max_delete_per_activation = 10000 audit_log_timeout_incomplete.period_secs = 600 audit_log_timeout_incomplete.timeout_secs = 14400 audit_log_timeout_incomplete.max_timed_out_per_activation = 1000 @@ -1609,6 +1624,10 @@ mod test { period_secs: Duration::from_secs(300), max_delete_per_activation: 10_000, }, + token_cleanup: TokenCleanupConfig { + period_secs: Duration::from_secs(300), + max_delete_per_activation: 10_000, + }, audit_log_timeout_incomplete: AuditLogTimeoutIncompleteConfig { period_secs: Duration::from_secs(600), @@ -1729,6 +1748,8 @@ mod test { attached_subnet_manager.period_secs = 60 session_cleanup.period_secs = 300 session_cleanup.max_delete_per_activation = 10000 + token_cleanup.period_secs = 300 + token_cleanup.max_delete_per_activation = 10000 audit_log_timeout_incomplete.period_secs = 600 audit_log_timeout_incomplete.timeout_secs = 14400 audit_log_timeout_incomplete.max_timed_out_per_activation = 1000 diff --git a/nexus/background-task-interface/src/init.rs b/nexus/background-task-interface/src/init.rs index 62b9f0c66df..ca6c554e311 100644 --- a/nexus/background-task-interface/src/init.rs +++ b/nexus/background-task-interface/src/init.rs @@ -62,6 +62,7 @@ pub struct BackgroundTasks { pub task_trust_quorum_manager: Activator, pub task_attached_subnet_manager: Activator, pub task_session_cleanup: Activator, + pub task_token_cleanup: Activator, // Handles to activate background tasks that do not get used by Nexus // at-large. These background tasks are implementation details as far as diff --git a/nexus/db-model/src/schema_versions.rs b/nexus/db-model/src/schema_versions.rs index 7aa942766e2..f76b5f12a94 100644 --- a/nexus/db-model/src/schema_versions.rs +++ b/nexus/db-model/src/schema_versions.rs @@ -16,7 +16,7 @@ use std::{collections::BTreeMap, sync::LazyLock}; /// /// This must be updated when you change the database schema. Refer to /// schema/crdb/README.adoc in the root of this repository for details. -pub const SCHEMA_VERSION: Version = Version::new(257, 0, 0); +pub const SCHEMA_VERSION: Version = Version::new(258, 0, 0); /// List of all past database schema versions, in *reverse* order /// @@ -28,6 +28,7 @@ pub static KNOWN_VERSIONS: LazyLock> = LazyLock::new(|| { // | leaving the first copy as an example for the next person. // v // KnownVersion::new(next_int, "unique-dirname-with-the-sql-files"), + KnownVersion::new(258, "device-access-token-time-expires-index"), KnownVersion::new(257, "add-disk-adoption-requests"), KnownVersion::new(256, "bgp-unnumbered-peer-cleanup"), KnownVersion::new(255, "blueprint-add-external-networking-generation"), diff --git a/nexus/db-queries/src/db/datastore/device_auth.rs b/nexus/db-queries/src/db/datastore/device_auth.rs index 07e7fbc31df..704c4eef427 100644 --- a/nexus/db-queries/src/db/datastore/device_auth.rs +++ b/nexus/db-queries/src/db/datastore/device_auth.rs @@ -12,8 +12,11 @@ use crate::db::model::DeviceAuthRequest; use crate::db::model::to_db_typed_uuid; use crate::db::pagination::paginated; use async_bb8_diesel::AsyncRunQueryDsl; +use chrono::DateTime; use chrono::Utc; +use diesel::dsl::sql_query; use diesel::prelude::*; +use diesel::sql_types; use nexus_db_errors::ErrorHandler; use nexus_db_errors::public_error_from_diesel; use nexus_db_schema::schema::device_access_token; @@ -287,6 +290,30 @@ impl DataStore { Ok(()) } + /// Hard-delete up to `limit` device access tokens whose `time_expires` is + /// non-NULL and older than `cutoff`, returning the number deleted. Tokens + /// with NULL `time_expires` never expire and are not eligible. + pub async fn token_cleanup_batch( + &self, + opctx: &OpContext, + cutoff: DateTime, + limit: u32, + ) -> Result { + opctx.authorize(authz::Action::Modify, &authz::FLEET).await?; + + sql_query( + "DELETE FROM omicron.public.device_access_token \ + WHERE time_expires IS NOT NULL AND time_expires < $1 \ + ORDER BY time_expires \ + LIMIT $2", + ) + .bind::(cutoff) + .bind::(i64::from(limit)) + .execute_async(&*self.pool_connection_authorized(opctx).await?) + .await + .map_err(|e| public_error_from_diesel(e, ErrorHandler::Server)) + } + /// Delete all tokens for the user pub async fn silo_user_tokens_delete( &self, diff --git a/nexus/examples/config-second.toml b/nexus/examples/config-second.toml index 6196f141184..c4f6bbddcee 100644 --- a/nexus/examples/config-second.toml +++ b/nexus/examples/config-second.toml @@ -198,6 +198,8 @@ trust_quorum.period_secs = 60 attached_subnet_manager.period_secs = 60 session_cleanup.period_secs = 300 session_cleanup.max_delete_per_activation = 10000 +token_cleanup.period_secs = 300 +token_cleanup.max_delete_per_activation = 10000 audit_log_timeout_incomplete.period_secs = 600 audit_log_timeout_incomplete.timeout_secs = 14400 audit_log_timeout_incomplete.max_timed_out_per_activation = 1000 diff --git a/nexus/examples/config.toml b/nexus/examples/config.toml index 150d6943170..dcc6917be2b 100644 --- a/nexus/examples/config.toml +++ b/nexus/examples/config.toml @@ -182,6 +182,8 @@ trust_quorum.period_secs = 60 attached_subnet_manager.period_secs = 60 session_cleanup.period_secs = 300 session_cleanup.max_delete_per_activation = 10000 +token_cleanup.period_secs = 300 +token_cleanup.max_delete_per_activation = 10000 audit_log_timeout_incomplete.period_secs = 600 audit_log_timeout_incomplete.timeout_secs = 14400 audit_log_timeout_incomplete.max_timed_out_per_activation = 1000 diff --git a/nexus/src/app/background/init.rs b/nexus/src/app/background/init.rs index bdf9ee01638..a4e1bc01e37 100644 --- a/nexus/src/app/background/init.rs +++ b/nexus/src/app/background/init.rs @@ -138,6 +138,7 @@ use super::tasks::session_cleanup; use super::tasks::support_bundle_collector; use super::tasks::sync_service_zone_nat::ServiceZoneNatTracker; use super::tasks::sync_switch_configuration::SwitchPortSettingsManager; +use super::tasks::token_cleanup; use super::tasks::trust_quorum; use super::tasks::tuf_artifact_replication; use super::tasks::tuf_repo_pruner; @@ -277,6 +278,7 @@ impl BackgroundTasksInitializer { task_trust_quorum_manager: Activator::new(), task_attached_subnet_manager: Activator::new(), task_session_cleanup: Activator::new(), + task_token_cleanup: Activator::new(), // Handles to activate background tasks that do not get used by Nexus // at-large. These background tasks are implementation details as far as @@ -370,6 +372,7 @@ impl BackgroundTasksInitializer { task_trust_quorum_manager, task_attached_subnet_manager, task_session_cleanup, + task_token_cleanup, task_audit_log_timeout_incomplete, task_audit_log_cleanup, // Add new background tasks here. Be sure to use this binding in a @@ -1247,6 +1250,19 @@ impl BackgroundTasksInitializer { activator: task_session_cleanup, }); + driver.register(TaskDefinition { + name: "token_cleanup", + description: "hard-deletes expired device access tokens", + period: config.token_cleanup.period_secs, + task_impl: Box::new(token_cleanup::TokenCleanup::new( + datastore.clone(), + config.token_cleanup.max_delete_per_activation, + )), + opctx: opctx.child(BTreeMap::new()), + watchers: vec![], + activator: task_token_cleanup, + }); + driver.register(TaskDefinition { name: "audit_log_timeout_incomplete", description: "transitions stale incomplete audit log entries to \ diff --git a/nexus/src/app/background/tasks/mod.rs b/nexus/src/app/background/tasks/mod.rs index fdcb45ef8d0..fb991ff7d75 100644 --- a/nexus/src/app/background/tasks/mod.rs +++ b/nexus/src/app/background/tasks/mod.rs @@ -53,6 +53,7 @@ pub mod support_bundle; pub mod support_bundle_collector; pub mod sync_service_zone_nat; pub mod sync_switch_configuration; +pub mod token_cleanup; pub mod trust_quorum; pub mod tuf_artifact_replication; pub mod tuf_repo_pruner; diff --git a/nexus/src/app/background/tasks/token_cleanup.rs b/nexus/src/app/background/tasks/token_cleanup.rs new file mode 100644 index 00000000000..0bfd0cb3ad0 --- /dev/null +++ b/nexus/src/app/background/tasks/token_cleanup.rs @@ -0,0 +1,233 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Background task that hard-deletes expired device access tokens. + +use crate::app::background::BackgroundTask; +use chrono::Utc; +use futures::future::BoxFuture; +use nexus_db_queries::context::OpContext; +use nexus_db_queries::db::DataStore; +use nexus_types::internal_api::background::TokenCleanupStatus; +use serde_json::json; +use std::sync::Arc; + +pub struct TokenCleanup { + datastore: Arc, + max_delete_per_activation: u32, +} + +impl TokenCleanup { + pub fn new( + datastore: Arc, + max_delete_per_activation: u32, + ) -> Self { + Self { datastore, max_delete_per_activation } + } + + pub(crate) async fn actually_activate( + &mut self, + opctx: &OpContext, + ) -> TokenCleanupStatus { + // Token expiration is encoded per-row in `time_expires`, so the + // eligibility cutoff is simply "now". Tokens with NULL `time_expires` + // never expire and are not affected. + let cutoff = Utc::now(); + let limit = self.max_delete_per_activation; + let (deleted, error) = match self + .datastore + .token_cleanup_batch(opctx, cutoff, limit) + .await + { + Ok(deleted) => { + if deleted > 0 { + slog::info!( + &opctx.log, + "token cleanup deleted {deleted} expired tokens"; + "cutoff" => %cutoff, + "limit" => limit, + ); + } else { + slog::debug!( + &opctx.log, + "token cleanup found no expired tokens"; + "cutoff" => %cutoff, + ); + } + (deleted, None) + } + Err(err) => { + let msg = format!("token cleanup failed: {err:#}"); + slog::error!(&opctx.log, "{msg}"); + (0, Some(msg)) + } + }; + + TokenCleanupStatus { deleted, cutoff, limit, error } + } +} + +impl BackgroundTask for TokenCleanup { + fn activate<'a>( + &'a mut self, + opctx: &'a OpContext, + ) -> BoxFuture<'a, serde_json::Value> { + Box::pin(async { + let status = self.actually_activate(opctx).await; + match serde_json::to_value(status) { + Ok(val) => val, + Err(err) => { + json!({ "error": format!("failed to serialize status: {err}") }) + } + } + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use async_bb8_diesel::AsyncRunQueryDsl; + use chrono::TimeDelta; + use chrono::Utc; + use diesel::prelude::*; + use nexus_db_model::DeviceAccessToken; + use nexus_db_queries::db::pub_test_utils::TestDatabase; + use nexus_db_schema::schema::device_access_token; + use omicron_test_utils::dev; + use omicron_uuid_kinds::GenericUuid; + use omicron_uuid_kinds::SiloUserUuid; + use uuid::Uuid; + + /// Insert a token directly via diesel, bypassing the datastore's transaction + /// path (which requires a matching device auth request). + async fn insert_token(datastore: &DataStore, token: DeviceAccessToken) { + let conn = datastore.pool_connection_for_tests().await.unwrap(); + diesel::insert_into(device_access_token::table) + .values(token) + .execute_async(&*conn) + .await + .unwrap(); + } + + fn make_token( + silo_user_id: SiloUserUuid, + time_expires: Option>, + ) -> DeviceAccessToken { + // `new()` asserts time_expires is in the future, so construct with a + // future expiry and overwrite afterward. + let placeholder = Utc::now() + TimeDelta::try_hours(1).unwrap(); + // `device_code` column is STRING(40); a bare UUID (36 chars) fits. + let mut token = DeviceAccessToken::new( + Uuid::new_v4(), + Uuid::new_v4().to_string(), + Utc::now(), + silo_user_id, + Some(placeholder), + ); + token.time_expires = time_expires; + token + } + + #[tokio::test] + async fn test_token_cleanup_activation() { + let logctx = dev::test_setup_log("test_token_cleanup_activation"); + let db = TestDatabase::new_with_datastore(&logctx.log).await; + let (opctx, datastore) = (db.opctx(), db.datastore()); + + let silo_user_id = SiloUserUuid::new_v4(); + let now = Utc::now(); + + // Expired an hour ago — should be cleaned up. + let expired = make_token( + silo_user_id, + Some(now - TimeDelta::try_hours(1).unwrap()), + ); + let expired_id = expired.id(); + insert_token(datastore, expired).await; + + // Expires in an hour — should be preserved. + let live = make_token( + silo_user_id, + Some(now + TimeDelta::try_hours(1).unwrap()), + ); + let live_id = live.id(); + insert_token(datastore, live).await; + + // Never expires — should be preserved. + let perpetual = make_token(silo_user_id, None); + let perpetual_id = perpetual.id(); + insert_token(datastore, perpetual).await; + + let mut task = TokenCleanup::new(datastore.clone(), 10_000); + + let status = task.actually_activate(opctx).await; + assert_eq!(status.deleted, 1); + assert!(status.error.is_none()); + assert_eq!(status.limit, 10_000); + + // Second activation should find nothing. + let status = task.actually_activate(opctx).await; + assert_eq!(status.deleted, 0); + assert!(status.error.is_none()); + + // Verify which rows remain — look up by ID to avoid full table scans. + let conn = datastore.pool_connection_for_tests().await.unwrap(); + for (id, should_exist) in + [(expired_id, false), (live_id, true), (perpetual_id, true)] + { + let found: Option = device_access_token::table + .filter(device_access_token::id.eq(id.into_untyped_uuid())) + .select(DeviceAccessToken::as_select()) + .first_async(&*conn) + .await + .optional() + .unwrap(); + assert_eq!( + found.is_some(), + should_exist, + "token {id} exists?={should_exist:?}", + ); + } + + db.terminate().await; + logctx.cleanup_successful(); + } + + #[tokio::test] + async fn test_token_cleanup_respects_limit() { + let logctx = dev::test_setup_log("test_token_cleanup_respects_limit"); + let db = TestDatabase::new_with_datastore(&logctx.log).await; + let (opctx, datastore) = (db.opctx(), db.datastore()); + + let silo_user_id = SiloUserUuid::new_v4(); + let now = Utc::now(); + + // Create 5 expired tokens. + for i in 0..5 { + let token = make_token( + silo_user_id, + Some(now - TimeDelta::try_hours(1 + i).unwrap()), + ); + insert_token(datastore, token).await; + } + + let mut task = TokenCleanup::new(datastore.clone(), 2); + + let status = task.actually_activate(opctx).await; + assert_eq!(status.deleted, 2); + + let status = task.actually_activate(opctx).await; + assert_eq!(status.deleted, 2); + + let status = task.actually_activate(opctx).await; + assert_eq!(status.deleted, 1); + + let status = task.actually_activate(opctx).await; + assert_eq!(status.deleted, 0); + + db.terminate().await; + logctx.cleanup_successful(); + } +} diff --git a/nexus/tests/config.test.toml b/nexus/tests/config.test.toml index d0e418398fd..343905c2060 100644 --- a/nexus/tests/config.test.toml +++ b/nexus/tests/config.test.toml @@ -219,6 +219,8 @@ trust_quorum.period_secs = 60 attached_subnet_manager.period_secs = 60 session_cleanup.period_secs = 300 session_cleanup.max_delete_per_activation = 10000 +token_cleanup.period_secs = 300 +token_cleanup.max_delete_per_activation = 10000 audit_log_timeout_incomplete.period_secs = 600 audit_log_timeout_incomplete.timeout_secs = 14400 audit_log_timeout_incomplete.max_timed_out_per_activation = 1000 diff --git a/nexus/types/src/internal_api/background.rs b/nexus/types/src/internal_api/background.rs index 74ac1a8302d..95927ec48e3 100644 --- a/nexus/types/src/internal_api/background.rs +++ b/nexus/types/src/internal_api/background.rs @@ -1162,6 +1162,20 @@ pub struct SessionCleanupStatus { pub error: Option, } +/// The status of a `token_cleanup` background task activation. +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] +pub struct TokenCleanupStatus { + /// Number of tokens deleted in this activation. + pub deleted: usize, + /// The cutoff time used: tokens whose `time_expires` is non-NULL and older + /// than this were eligible. + pub cutoff: DateTime, + /// The per-activation delete limit. + pub limit: u32, + /// Errors encountered during this activation. + pub error: Option, +} + /// Status of the background task pushing service firewall rules. #[derive(Default, Deserialize, Serialize)] pub struct ServiceFirewallRuleStatus { diff --git a/schema/crdb/dbinit.sql b/schema/crdb/dbinit.sql index 9be0e8b1693..20ac2176522 100644 --- a/schema/crdb/dbinit.sql +++ b/schema/crdb/dbinit.sql @@ -3429,6 +3429,11 @@ CREATE UNIQUE INDEX IF NOT EXISTS device_access_token_unique CREATE INDEX IF NOT EXISTS lookup_device_access_token_by_silo_user ON omicron.public.device_access_token (silo_user_id); +-- Used by the token_cleanup background task to grab the next batch of expired +-- tokens efficiently. Non-unique is fine: we just delete the next N, repeat. +CREATE INDEX IF NOT EXISTS lookup_device_access_token_by_expiration + ON omicron.public.device_access_token (time_expires); + /* * Assignments between users, roles, and resources @@ -8576,7 +8581,7 @@ INSERT INTO omicron.public.db_metadata ( version, target_version ) VALUES - (TRUE, NOW(), NOW(), '257.0.0', NULL) + (TRUE, NOW(), NOW(), '258.0.0', NULL) ON CONFLICT DO NOTHING; COMMIT; diff --git a/schema/crdb/device-access-token-time-expires-index/up.sql b/schema/crdb/device-access-token-time-expires-index/up.sql new file mode 100644 index 00000000000..68b3c867c67 --- /dev/null +++ b/schema/crdb/device-access-token-time-expires-index/up.sql @@ -0,0 +1,2 @@ +CREATE INDEX IF NOT EXISTS lookup_device_access_token_by_expiration + ON omicron.public.device_access_token (time_expires); diff --git a/schema/crdb/device-access-token-time-expires-index/up.verify.sql b/schema/crdb/device-access-token-time-expires-index/up.verify.sql new file mode 100644 index 00000000000..9b8d382bbb9 --- /dev/null +++ b/schema/crdb/device-access-token-time-expires-index/up.verify.sql @@ -0,0 +1,2 @@ +-- DO NOT EDIT. Generated by test_migration_verification_files. +SELECT CAST(IF((SELECT true WHERE EXISTS (SELECT index_name FROM omicron.crdb_internal.table_indexes WHERE descriptor_name = 'device_access_token' AND index_name = 'lookup_device_access_token_by_expiration')),'true','Schema change verification failed: index lookup_device_access_token_by_expiration on table device_access_token does not exist') AS BOOL); diff --git a/smf/nexus/multi-sled/config-partial.toml b/smf/nexus/multi-sled/config-partial.toml index 7a43386e375..3b4d21db0aa 100644 --- a/smf/nexus/multi-sled/config-partial.toml +++ b/smf/nexus/multi-sled/config-partial.toml @@ -121,6 +121,8 @@ trust_quorum.period_secs = 60 attached_subnet_manager.period_secs = 60 session_cleanup.period_secs = 300 session_cleanup.max_delete_per_activation = 10000 +token_cleanup.period_secs = 300 +token_cleanup.max_delete_per_activation = 10000 audit_log_timeout_incomplete.period_secs = 600 audit_log_timeout_incomplete.timeout_secs = 14400 audit_log_timeout_incomplete.max_timed_out_per_activation = 1000 diff --git a/smf/nexus/single-sled/config-partial.toml b/smf/nexus/single-sled/config-partial.toml index 11863e1c681..d50fb544456 100644 --- a/smf/nexus/single-sled/config-partial.toml +++ b/smf/nexus/single-sled/config-partial.toml @@ -121,6 +121,8 @@ multicast_reconciler.period_secs = 60 attached_subnet_manager.period_secs = 60 session_cleanup.period_secs = 300 session_cleanup.max_delete_per_activation = 10000 +token_cleanup.period_secs = 300 +token_cleanup.max_delete_per_activation = 10000 audit_log_timeout_incomplete.period_secs = 600 audit_log_timeout_incomplete.timeout_secs = 14400 audit_log_timeout_incomplete.max_timed_out_per_activation = 1000