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}