|
| 1 | +// Copyright 2021-Present Datadog, Inc. |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | + |
| 15 | +use bytesize::ByteSize; |
| 16 | +use quickwit_common::fs::get_disk_info; |
| 17 | +use quickwit_config::NodeConfig; |
| 18 | +use quickwit_config::service::QuickwitService; |
| 19 | +use tracing::warn; |
| 20 | + |
| 21 | +/// A list of all the known disk budgets. |
| 22 | +/// |
| 23 | +/// External and unbounded disk usage, e.g. the indexing workbench (`indexing/`) and the delete |
| 24 | +/// task workbench (`delete_task_service/`), are not included. |
| 25 | +#[derive(Default, Debug, Eq, PartialEq)] |
| 26 | +struct ExpectedDiskUsage { |
| 27 | + // indexer / ingester |
| 28 | + split_store_max_num_bytes: Option<ByteSize>, |
| 29 | + max_queue_disk_usage: Option<ByteSize>, |
| 30 | + // searcher |
| 31 | + split_cache: Option<ByteSize>, |
| 32 | +} |
| 33 | + |
| 34 | +impl ExpectedDiskUsage { |
| 35 | + fn from_config(node_config: &NodeConfig) -> Self { |
| 36 | + let mut expected = Self::default(); |
| 37 | + if node_config.is_service_enabled(QuickwitService::Indexer) { |
| 38 | + expected.max_queue_disk_usage = |
| 39 | + Some(node_config.ingest_api_config.max_queue_disk_usage); |
| 40 | + expected.split_store_max_num_bytes = |
| 41 | + Some(node_config.indexer_config.split_store_max_num_bytes); |
| 42 | + } |
| 43 | + if node_config.is_service_enabled(QuickwitService::Searcher) { |
| 44 | + expected.split_cache = node_config |
| 45 | + .searcher_config |
| 46 | + .split_cache |
| 47 | + .map(|limits| limits.max_num_bytes); |
| 48 | + } |
| 49 | + expected |
| 50 | + } |
| 51 | + |
| 52 | + fn total(&self) -> ByteSize { |
| 53 | + self.split_store_max_num_bytes.unwrap_or_default() |
| 54 | + + self.max_queue_disk_usage.unwrap_or_default() |
| 55 | + + self.split_cache.unwrap_or_default() |
| 56 | + } |
| 57 | +} |
| 58 | + |
| 59 | +pub(super) fn check_data_dir_disk_usage(node_config: &NodeConfig) { |
| 60 | + let Some(disk_info) = get_disk_info(&node_config.data_dir_path) else { |
| 61 | + return; |
| 62 | + }; |
| 63 | + let expected_disk_usage = ExpectedDiskUsage::from_config(node_config); |
| 64 | + if expected_disk_usage.total() > disk_info.total_space { |
| 65 | + warn!( |
| 66 | + data_dir = %node_config.data_dir_path.display(), |
| 67 | + device = %disk_info.device_name, |
| 68 | + mount_point = %disk_info.mount_point.display(), |
| 69 | + volume_size = ?disk_info.total_space, |
| 70 | + ?expected_disk_usage, |
| 71 | + "data dir volume too small" |
| 72 | + ); |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +#[cfg(test)] |
| 77 | +mod tests { |
| 78 | + use std::collections::HashSet; |
| 79 | + |
| 80 | + use super::*; |
| 81 | + |
| 82 | + #[test] |
| 83 | + fn test_searcher_without_split_cache_has_no_expected_disk_usage() { |
| 84 | + let mut node_config = NodeConfig::for_test(); |
| 85 | + node_config.enabled_services = HashSet::from([QuickwitService::Searcher]); |
| 86 | + node_config.searcher_config.split_cache = None; |
| 87 | + |
| 88 | + let expected_disk_usage = ExpectedDiskUsage::from_config(&node_config); |
| 89 | + |
| 90 | + assert_eq!(expected_disk_usage, ExpectedDiskUsage::default()); |
| 91 | + } |
| 92 | + |
| 93 | + #[test] |
| 94 | + fn test_expected_disk_usage_aggregates_enabled_service_budgets() { |
| 95 | + let mut node_config = NodeConfig::for_test(); |
| 96 | + node_config.enabled_services = |
| 97 | + HashSet::from([QuickwitService::Indexer, QuickwitService::Searcher]); |
| 98 | + |
| 99 | + let expected_disk_usage = ExpectedDiskUsage::from_config(&node_config); |
| 100 | + let expected_total = node_config.indexer_config.split_store_max_num_bytes |
| 101 | + + node_config.ingest_api_config.max_queue_disk_usage |
| 102 | + + node_config |
| 103 | + .searcher_config |
| 104 | + .split_cache |
| 105 | + .map(|limits| limits.max_num_bytes) |
| 106 | + .unwrap_or_default(); |
| 107 | + |
| 108 | + assert_eq!(expected_disk_usage.total(), expected_total); |
| 109 | + } |
| 110 | +} |
0 commit comments