1use rocksdb::{
2 Env,
3 event_listener::{
4 CompactionJobInfo, DBBackgroundErrorReason, DBWriteStallCondition, EventListener,
5 FlushJobInfo, IngestionInfo, MemTableInfo, MutableStatus, SubcompactionJobInfo,
6 WriteStallInfo,
7 },
8};
9use tuwunel_core::{Config, debug, debug::INFO_SPAN_LEVEL, debug_info, error, info, warn};
10
11pub(super) struct Events;
16
17impl Events {
18 pub(super) fn new(_config: &Config, _env: &Env) -> Self { Self {} }
19}
20
21impl EventListener for Events {
22 #[tracing::instrument(name = "error", level = "error", skip_all)]
23 fn on_background_error(&self, reason: DBBackgroundErrorReason, _status: MutableStatus) {
24 error!(error = ?reason, "Critical RocksDB Error");
25 }
26
27 #[tracing::instrument(name = "stall", level = "warn", skip_all)]
28 fn on_stall_conditions_changed(&self, info: &WriteStallInfo) {
29 let col = info.cf_name();
30 let col = col
31 .as_deref()
32 .map(str::from_utf8)
33 .expect("column has a name")
34 .expect("column name is valid utf8");
35
36 let prev = info.prev();
37 match info.cur() {
38 | DBWriteStallCondition::KStopped => {
39 error!(?col, ?prev, "Database Stalled");
40 },
41 | DBWriteStallCondition::KDelayed if prev == DBWriteStallCondition::KStopped => {
42 warn!(?col, ?prev, "Database Stall Recovering");
43 },
44 | DBWriteStallCondition::KDelayed => {
45 warn!(?col, ?prev, "Database Stalling");
46 },
47 | DBWriteStallCondition::KNormal
48 if prev == DBWriteStallCondition::KStopped
49 || prev == DBWriteStallCondition::KDelayed =>
50 {
51 info!(?col, ?prev, "Database Stall Recovered");
52 },
53 | DBWriteStallCondition::KNormal => {
54 debug!(?col, ?prev, "Database Normal");
55 },
56 }
57 }
58
59 #[tracing::instrument(
60 name = "compaction",
61 level = INFO_SPAN_LEVEL,
62 skip_all,
63 )]
64 fn on_compaction_begin(&self, info: &CompactionJobInfo) {
65 let col = info.cf_name();
66 let col = col
67 .as_deref()
68 .map(str::from_utf8)
69 .expect("column has a name")
70 .expect("column name is valid utf8");
71
72 let level = (info.base_input_level(), info.output_level());
73 let records = (info.input_records(), info.output_records());
74 let bytes = (info.total_input_bytes(), info.total_output_bytes());
75 let files = (
76 info.input_file_count(),
77 info.output_file_count(),
78 info.num_input_files_at_output_level(),
79 );
80
81 debug!(
82 status = ?info.status(),
83 ?level,
84 ?files,
85 ?records,
86 ?bytes,
87 micros = info.elapsed_micros(),
88 errs = info.num_corrupt_keys(),
89 reason = ?info.compaction_reason(),
90 ?col,
91 "Compaction Starting",
92 );
93 }
94
95 #[tracing::instrument(
96 name = "compaction",
97 level = INFO_SPAN_LEVEL,
98 skip_all,
99 )]
100 fn on_compaction_completed(&self, info: &CompactionJobInfo) {
101 let col = info.cf_name();
102 let col = col
103 .as_deref()
104 .map(str::from_utf8)
105 .expect("column has a name")
106 .expect("column name is valid utf8");
107
108 let level = (info.base_input_level(), info.output_level());
109 let records = (info.input_records(), info.output_records());
110 let bytes = (info.total_input_bytes(), info.total_output_bytes());
111 let files = (
112 info.input_file_count(),
113 info.output_file_count(),
114 info.num_input_files_at_output_level(),
115 );
116
117 debug_info!(
118 status = ?info.status(),
119 ?level,
120 ?files,
121 ?records,
122 ?bytes,
123 micros = info.elapsed_micros(),
124 errs = info.num_corrupt_keys(),
125 reason = ?info.compaction_reason(),
126 ?col,
127 "Compaction Complete",
128 );
129 }
130
131 #[tracing::instrument(name = "compaction", level = "debug", skip_all)]
132 fn on_subcompaction_begin(&self, info: &SubcompactionJobInfo) {
133 let col = info.cf_name();
134 let col = col
135 .as_deref()
136 .map(str::from_utf8)
137 .expect("column has a name")
138 .expect("column name is valid utf8");
139
140 let level = (info.base_input_level(), info.output_level());
141
142 debug!(
143 status = ?info.status(),
144 ?level,
145 tid = info.thread_id(),
146 reason = ?info.compaction_reason(),
147 ?col,
148 "Compaction Starting",
149 );
150 }
151
152 #[tracing::instrument(name = "compaction", level = "debug", skip_all)]
153 fn on_subcompaction_completed(&self, info: &SubcompactionJobInfo) {
154 let col = info.cf_name();
155 let col = col
156 .as_deref()
157 .map(str::from_utf8)
158 .expect("column has a name")
159 .expect("column name is valid utf8");
160
161 let level = (info.base_input_level(), info.output_level());
162
163 debug!(
164 status = ?info.status(),
165 ?level,
166 tid = info.thread_id(),
167 reason = ?info.compaction_reason(),
168 ?col,
169 "Compaction Complete",
170 );
171 }
172
173 #[tracing::instrument(
174 name = "flush",
175 level = INFO_SPAN_LEVEL,
176 skip_all,
177 )]
178 fn on_flush_begin(&self, info: &FlushJobInfo) {
179 let col = info.cf_name();
180 let col = col
181 .as_deref()
182 .map(str::from_utf8)
183 .expect("column has a name")
184 .expect("column name is valid utf8");
185
186 debug!(
187 seq_start = info.smallest_seqno(),
188 seq_end = info.largest_seqno(),
189 slow = info.triggered_writes_slowdown(),
190 stop = info.triggered_writes_stop(),
191 reason = ?info.flush_reason(),
192 ?col,
193 "Flush Starting",
194 );
195 }
196
197 #[tracing::instrument(
198 name = "flush",
199 level = INFO_SPAN_LEVEL,
200 skip_all,
201 )]
202 fn on_flush_completed(&self, info: &FlushJobInfo) {
203 let col = info.cf_name();
204 let col = col
205 .as_deref()
206 .map(str::from_utf8)
207 .expect("column has a name")
208 .expect("column name is valid utf8");
209
210 debug_info!(
211 seq_start = info.smallest_seqno(),
212 seq_end = info.largest_seqno(),
213 slow = info.triggered_writes_slowdown(),
214 stop = info.triggered_writes_stop(),
215 reason = ?info.flush_reason(),
216 ?col,
217 "Flush Complete",
218 );
219 }
220
221 #[tracing::instrument(
222 name = "memtable",
223 level = INFO_SPAN_LEVEL,
224 skip_all,
225 )]
226 fn on_memtable_sealed(&self, info: &MemTableInfo) {
227 let col = info.cf_name();
228 let col = col
229 .as_deref()
230 .map(str::from_utf8)
231 .expect("column has a name")
232 .expect("column name is valid utf8");
233
234 debug_info!(
235 seq_first = info.first_seqno(),
236 seq_early = info.earliest_seqno(),
237 ents = info.num_entries(),
238 dels = info.num_deletes(),
239 ?col,
240 "Buffer Filled",
241 );
242 }
243
244 fn on_external_file_ingested(&self, _info: &IngestionInfo) {
245 unimplemented!();
246 }
247}