Skip to content

Commit a5b1b01

Browse files
authored
Make standalone compactors config top-level (#6471)
1 parent 7054e1d commit a5b1b01

5 files changed

Lines changed: 67 additions & 77 deletions

File tree

quickwit/quickwit-config/src/node_config/mod.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,6 @@ pub struct IndexerConfig {
163163
pub enable_cooperative_indexing: bool,
164164
#[serde(default = "IndexerConfig::default_cpu_capacity")]
165165
pub cpu_capacity: CpuCapacity,
166-
/// When true, the compactor service is not implicitly started on indexer
167-
/// nodes. Dedicated compactor nodes must be deployed separately.
168-
/// When false (default), every indexer node also runs the compactor.
169-
#[serde(default = "IndexerConfig::default_enable_standalone_compactors")]
170-
pub enable_standalone_compactors: bool,
171166
/// If true, run Parquet merges through the streaming column-major engine
172167
/// (`execute_merge_operation`). If false (default), use the in-memory
173168
/// `merge_sorted_parquet_files` engine. The legacy in-memory engine is
@@ -216,10 +211,6 @@ impl IndexerConfig {
216211
CpuCapacity::one_cpu_thread() * (quickwit_common::num_cpus() as u32)
217212
}
218213

219-
fn default_enable_standalone_compactors() -> bool {
220-
false
221-
}
222-
223214
fn default_parquet_merge_use_streaming_engine() -> bool {
224215
false
225216
}
@@ -236,7 +227,6 @@ impl IndexerConfig {
236227
cpu_capacity: PIPELINE_FULL_CAPACITY * 4u32,
237228
max_merge_write_throughput: None,
238229
merge_concurrency: NonZeroUsize::new(3).unwrap(),
239-
enable_standalone_compactors: false,
240230
parquet_merge_use_streaming_engine: Self::default_parquet_merge_use_streaming_engine(),
241231
};
242232
Ok(indexer_config)
@@ -254,7 +244,6 @@ impl Default for IndexerConfig {
254244
cpu_capacity: Self::default_cpu_capacity(),
255245
merge_concurrency: Self::default_merge_concurrency(),
256246
max_merge_write_throughput: None,
257-
enable_standalone_compactors: Self::default_enable_standalone_compactors(),
258247
parquet_merge_use_streaming_engine: Self::default_parquet_merge_use_streaming_engine(),
259248
}
260249
}
@@ -861,6 +850,8 @@ pub struct NodeConfig {
861850
pub ingest_api_config: IngestApiConfig,
862851
pub jaeger_config: JaegerConfig,
863852
pub compactor_config: CompactorConfig,
853+
#[serde(skip_serializing)]
854+
pub enable_standalone_compactors: bool,
864855
}
865856

866857
impl NodeConfig {
@@ -925,6 +916,14 @@ impl NodeConfig {
925916
pub fn for_test_from_ports(rest_listen_port: u16, grpc_listen_port: u16) -> Self {
926917
serialize::node_config_for_tests_from_ports(rest_listen_port, grpc_listen_port)
927918
}
919+
920+
/// Test config with `enable_standalone_compactors = true`.
921+
#[cfg(any(test, feature = "testsuite"))]
922+
pub fn for_test_with_standalone_compactors() -> Self {
923+
let mut node_config = Self::for_test();
924+
node_config.enable_standalone_compactors = true;
925+
node_config
926+
}
928927
}
929928

930929
#[cfg(test)]

quickwit/quickwit-config/src/node_config/serialize.rs

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,7 @@ impl NodeConfigBuilder {
234234
let node_id = self.node_id.resolve(env_vars).map(NodeId::new)?;
235235
let availability_zone = self.availability_zone.resolve_optional(env_vars)?;
236236

237-
self.indexer_config.enable_standalone_compactors =
238-
self.enable_standalone_compactors.resolve(env_vars)?;
237+
let enable_standalone_compactors = self.enable_standalone_compactors.resolve(env_vars)?;
239238

240239
let enabled_services: HashSet<QuickwitService> = self
241240
.enabled_services
@@ -343,6 +342,7 @@ impl NodeConfigBuilder {
343342
ingest_api_config: self.ingest_api_config,
344343
jaeger_config: self.jaeger_config,
345344
compactor_config: self.compactor_config,
345+
enable_standalone_compactors,
346346
};
347347

348348
validate(&node_config)?;
@@ -361,7 +361,7 @@ fn validate(node_config: &NodeConfig) -> anyhow::Result<()> {
361361
warn!("peer seeds are empty");
362362
}
363363
if node_config.is_service_enabled(QuickwitService::Compactor)
364-
&& !node_config.indexer_config.enable_standalone_compactors
364+
&& !node_config.enable_standalone_compactors
365365
{
366366
bail!(
367367
"the `compactor` service can only be enabled when `enable_standalone_compactors` is \
@@ -552,6 +552,7 @@ pub fn node_config_for_tests_from_ports(
552552
ingest_api_config: IngestApiConfig::default(),
553553
jaeger_config: JaegerConfig::default(),
554554
compactor_config: CompactorConfig::default(),
555+
enable_standalone_compactors: false,
555556
}
556557
}
557558

@@ -681,7 +682,6 @@ mod tests {
681682
cpu_capacity: IndexerConfig::default_cpu_capacity(),
682683
enable_cooperative_indexing: false,
683684
max_merge_write_throughput: Some(ByteSize::mb(100)),
684-
enable_standalone_compactors: false,
685685
parquet_merge_use_streaming_engine: true,
686686
}
687687
);
@@ -977,6 +977,49 @@ mod tests {
977977
.unwrap();
978978
}
979979

980+
#[tokio::test]
981+
async fn test_compactor_service_requires_standalone_flag() {
982+
// Compactor service enabled while `enable_standalone_compactors` is off is an
983+
// invalid combination: the default indexer-local merge pipeline is in use, so a
984+
// dedicated compactor must not run.
985+
let error = NodeConfigBuilder {
986+
enabled_services: ConfigValue::for_test(List(vec![
987+
"indexer".to_string(),
988+
"compactor".to_string(),
989+
])),
990+
..Default::default()
991+
}
992+
.build_and_validate(&HashMap::new())
993+
.await
994+
.unwrap_err();
995+
assert!(
996+
error.to_string().contains("enable_standalone_compactors"),
997+
"expected error to mention `enable_standalone_compactors`, got: {error}",
998+
);
999+
}
1000+
1001+
#[tokio::test]
1002+
async fn test_compactor_service_with_standalone_flag_validates() {
1003+
// All services enabled, including the compactor, with the standalone flag on
1004+
// is a valid configuration.
1005+
let node_config = NodeConfigBuilder {
1006+
enabled_services: ConfigValue::for_test(List(vec![
1007+
"control_plane".to_string(),
1008+
"indexer".to_string(),
1009+
"searcher".to_string(),
1010+
"janitor".to_string(),
1011+
"metastore".to_string(),
1012+
"compactor".to_string(),
1013+
])),
1014+
enable_standalone_compactors: ConfigValue::for_test(true),
1015+
..Default::default()
1016+
}
1017+
.build_and_validate(&HashMap::new())
1018+
.await
1019+
.unwrap();
1020+
assert!(node_config.is_service_enabled(QuickwitService::Compactor));
1021+
}
1022+
9801023
#[tokio::test]
9811024
async fn test_peer_socket_addrs() {
9821025
{

quickwit/quickwit-integration-tests/src/test_utils/cluster_sandbox.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,6 @@ impl TestNodeConfig {
7676
tcp_listener_resolver.add_listener(grpc_tcp_listener).await;
7777
config.indexer_config.enable_otlp_endpoint = self.enable_otlp;
7878
config.enabled_services.clone_from(&self.services);
79-
if config.enabled_services.contains(&QuickwitService::Indexer)
80-
&& !config.indexer_config.enable_standalone_compactors
81-
{
82-
config.enabled_services.insert(QuickwitService::Compactor);
83-
}
8479
config.jaeger_config.enable_endpoint = true;
8580
config.cluster_id.clone_from(&cluster_id);
8681
config.node_id = NodeId::new(format!("test-node-{node_idx}"));

quickwit/quickwit-integration-tests/src/tests/basic_tests.rs

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -162,47 +162,3 @@ async fn test_multi_nodes_cluster() {
162162

163163
sandbox.shutdown().await.unwrap();
164164
}
165-
166-
#[tokio::test]
167-
async fn test_indexer_implicitly_runs_compactor() {
168-
quickwit_common::setup_logging_for_tests();
169-
let sandbox = ClusterSandboxBuilder::default()
170-
.add_node([QuickwitService::Searcher])
171-
.add_node([QuickwitService::Metastore])
172-
.add_node([QuickwitService::ControlPlane])
173-
.add_node([QuickwitService::Janitor])
174-
.add_node([QuickwitService::Indexer])
175-
.build_and_start()
176-
.await;
177-
178-
let cluster_snapshot = sandbox
179-
.rest_client(QuickwitService::Searcher)
180-
.cluster()
181-
.snapshot()
182-
.await
183-
.unwrap();
184-
assert_eq!(cluster_snapshot.ready_nodes.len(), 5);
185-
186-
// The indexer node should also advertise the compactor service.
187-
let indexer_node = cluster_snapshot
188-
.chitchat_state_snapshot
189-
.node_states
190-
.iter()
191-
.find(|node_state| {
192-
node_state
193-
.get("enabled_services")
194-
.map(|s| s.contains("indexer"))
195-
.unwrap_or(false)
196-
})
197-
.expect("indexer node not found in cluster state");
198-
let services = indexer_node.get("enabled_services").unwrap();
199-
assert!(
200-
services.contains("compactor"),
201-
"indexer node should implicitly run the compactor, got: {services}"
202-
);
203-
204-
sandbox.shutdown().await.unwrap();
205-
}
206-
207-
// TODO: add test_standalone_compactor_node once the test config builder
208-
// resolves QW_ENABLE_STANDALONE_COMPACTORS from the environment.

quickwit/quickwit-serve/src/lib.rs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ async fn get_compaction_planner_client_if_needed(
293293
Option<CompactionPlannerServiceClient>,
294294
Option<ActorHandle<CompactionPlanner>>,
295295
)> {
296-
if !node_config.indexer_config.enable_standalone_compactors {
296+
if !node_config.enable_standalone_compactors {
297297
return Ok((None, None));
298298
}
299299
let is_janitor = node_config.is_service_enabled(QuickwitService::Janitor);
@@ -649,12 +649,11 @@ pub async fn serve_quickwit(
649649
let indexing_service_opt = if node_config.is_service_enabled(QuickwitService::Indexer) {
650650
// if standalone compactors is enabled, indexing pipelines don't perform any merges.
651651
// if standalone compactors is disabled, indexing pipelines perform all merges as before.
652-
let merge_scheduler_mailbox_opt =
653-
if !node_config.indexer_config.enable_standalone_compactors {
654-
Some(spawn_merge_scheduler_service(&universe, &node_config))
655-
} else {
656-
None
657-
};
652+
let merge_scheduler_mailbox_opt = if !node_config.enable_standalone_compactors {
653+
Some(spawn_merge_scheduler_service(&universe, &node_config))
654+
} else {
655+
None
656+
};
658657

659658
let split_cache = indexing_split_cache.clone();
660659
let indexing_service = start_indexing_service(
@@ -852,7 +851,7 @@ pub async fn serve_quickwit(
852851
};
853852

854853
let compactor_supervisor_opt = if node_config.is_service_enabled(QuickwitService::Compactor)
855-
&& node_config.indexer_config.enable_standalone_compactors
854+
&& node_config.enable_standalone_compactors
856855
{
857856
let compaction_dir = node_config.data_dir_path.join("compaction");
858857
fs::create_dir_all(&compaction_dir)?;
@@ -1993,8 +1992,7 @@ mod tests {
19931992
let metastore = MetastoreServiceClient::from_mock(MockMetastoreService::new());
19941993

19951994
// Janitor + indexer with standalone compactors enabled: planner client is returned.
1996-
let mut node_config = NodeConfig::for_test();
1997-
node_config.indexer_config.enable_standalone_compactors = true;
1995+
let mut node_config = NodeConfig::for_test_with_standalone_compactors();
19981996
node_config.enabled_services =
19991997
HashSet::from([QuickwitService::Janitor, QuickwitService::Indexer]);
20001998
let (client_opt, handle_opt) =
@@ -2028,7 +2026,7 @@ mod tests {
20282026

20292027
// Standalone compactors disabled: short-circuit returns (None, None) regardless of
20302028
// which services are enabled.
2031-
node_config.indexer_config.enable_standalone_compactors = false;
2029+
node_config.enable_standalone_compactors = false;
20322030
node_config.enabled_services =
20332031
HashSet::from([QuickwitService::Janitor, QuickwitService::Indexer]);
20342032
let (client_opt, handle_opt) =
@@ -2050,8 +2048,7 @@ mod tests {
20502048
let universe = Universe::new();
20512049
let metastore = MetastoreServiceClient::from_mock(MockMetastoreService::new());
20522050

2053-
let mut node_config = NodeConfig::for_test();
2054-
node_config.indexer_config.enable_standalone_compactors = true;
2051+
let mut node_config = NodeConfig::for_test_with_standalone_compactors();
20552052
node_config.enabled_services =
20562053
HashSet::from([QuickwitService::Indexer, QuickwitService::Compactor]);
20572054
let result =

0 commit comments

Comments
 (0)