Skip to main content

common_wal/config/
object_store.rs

1// Copyright 2023 Greptime Team
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
15use std::time::Duration;
16
17use common_base::readable_size::ReadableSize;
18use serde::{Deserialize, Serialize};
19
20/// Generation of the node prefix in standalone mode.
21///
22/// Standalone runs a single datanode that never changes generation, so the
23/// prefix layout carries the constant. Distributed mode will take both the
24/// node id and the generation from the metasrv.
25pub const STANDALONE_GENERATION: u64 = 0;
26
27/// When an append to the object store WAL returns.
28#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
29#[serde(rename_all = "snake_case")]
30pub enum AckMode {
31    /// An append returns after the object holding its entries is durable.
32    #[default]
33    Durable,
34    /// An append returns once its entries are admitted and their ids are
35    /// assigned; the object is created in the background.
36    Enqueued,
37}
38
39/// What a read does with a segment that still does not decode after a
40/// second fetch, because its checksum does not match or its content
41/// disagrees with its footer entry.
42#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
43#[serde(rename_all = "snake_case")]
44pub enum CorruptedSegmentAction {
45    /// The segment is skipped and recorded as a hole of its region; the other
46    /// regions of the object are unaffected.
47    #[default]
48    Skip,
49    /// The read fails.
50    Fail,
51}
52
53/// Object store wal configurations for datanode.
54#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
55#[serde(default)]
56pub struct ObjectStoreWalConfig {
57    /// Name of the storage provider that holds the WAL objects.
58    /// An empty name selects the default object store.
59    pub storage_provider: String,
60    /// Path prefix of the WAL objects inside the storage provider.
61    ///
62    /// The store runs under a node prefix derived from it, see
63    /// [`node_prefix`](Self::node_prefix).
64    pub prefix: String,
65    /// Interval of flushing buffered entries to the object store, at least
66    /// 10ms, defaults to 100ms.
67    #[serde(with = "humantime_serde")]
68    pub flush_interval: Duration,
69    /// The max size of a single batch object, defaults to 8MiB.
70    pub max_batch_bytes: ReadableSize,
71    /// When an append returns, defaults to `durable`.
72    pub ack_mode: AckMode,
73    /// Size of the unpersisted backlog at which appends stall until an
74    /// upload completes in `enqueued` mode, defaults to 64MiB.
75    pub max_unpersisted_bytes: ReadableSize,
76    /// Age of the oldest unpersisted entry at which appends stall until an
77    /// upload completes in `enqueued` mode, defaults to 8s.
78    #[serde(with = "humantime_serde")]
79    pub max_unpersisted_age: Duration,
80    /// What a read does with a segment that still does not decode after a
81    /// second fetch, because its checksum does not match or its content
82    /// disagrees with its footer entry, defaults to `skip`.
83    pub on_corrupted_segment: CorruptedSegmentAction,
84    /// Whether a region whose WAL history the configured location does not
85    /// hold opens anyway, with its writes that were not flushed lost, instead
86    /// of failing to open, defaults to false. Meant for one start after the
87    /// old WAL location is lost.
88    pub accept_missing_history: bool,
89}
90
91impl Default for ObjectStoreWalConfig {
92    fn default() -> Self {
93        Self {
94            storage_provider: String::new(),
95            prefix: "wal".to_string(),
96            flush_interval: Duration::from_millis(100),
97            max_batch_bytes: ReadableSize::mb(8),
98            ack_mode: AckMode::Durable,
99            max_unpersisted_bytes: ReadableSize::mb(64),
100            max_unpersisted_age: Duration::from_secs(8),
101            on_corrupted_segment: CorruptedSegmentAction::Skip,
102            accept_missing_history: false,
103        }
104    }
105}
106
107impl ObjectStoreWalConfig {
108    /// Returns the prefix the store of `node_id` runs under in `generation`:
109    /// `<prefix>/datanodes/<node_id>/epochs/<generation>`.
110    ///
111    /// Every place that names the store's prefix, whether to open the store or
112    /// to allocate the WAL options of a region, must derive it here so the
113    /// prefix a region persists matches the one its store runs under.
114    pub fn node_prefix(&self, node_id: u64, generation: u64) -> String {
115        format!("{}/datanodes/{node_id}/epochs/{generation}", self.prefix)
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122
123    #[test]
124    fn test_node_prefix() {
125        let config = ObjectStoreWalConfig::default();
126        assert_eq!(
127            config.node_prefix(0, STANDALONE_GENERATION),
128            "wal/datanodes/0/epochs/0"
129        );
130
131        let config = ObjectStoreWalConfig {
132            prefix: "cluster-a/wal".to_string(),
133            ..Default::default()
134        };
135        assert_eq!(
136            config.node_prefix(3, 7),
137            "cluster-a/wal/datanodes/3/epochs/7"
138        );
139    }
140}