Skip to content

Commit 7d55fa8

Browse files
committed
fix: resolve code-quality issues #396-#401
Eliminate panic!/unreachable!/unimplemented! from production paths and harden runtime behavior across the workspace. - #396: replace panic/unreachable in production paths with Result propagation or graceful degradation: - executor.rs: degrade to error reply instead of panicking on send - baseline.rs: from_u8 returns Option, state() falls back to Abandoned - redis_lists.rs: return Err via OptionNoneSnafu instead of unreachable - checkpoint.rs: return io::Error instead of unreachable - encode.rs: fall back to "txt" for invalid VerbatimString format - network.rs: log + fallback endpoint instead of panicking on bad addr - storage_murmur3.rs: add message to unreachable - #397: SlotIndexer::reshard_slots returns Err instead of unimplemented! - #398: net/pipeline switches unbounded mpsc to bounded channel with backpressure (config.command_queue_size) - #399: optimized_handler captures client write errors and cleans up the connection instead of silently dropping - #400: tag ownerless TODO/FIXME with #400 tracking id - #401: add #![allow(clippy::unwrap_used)] to raft/storage test modules, eliminating 135 clippy errors so `cargo clippy --all-targets` passes Also cleans up pre-existing workspace clippy warnings (complex type alias, doc indentation, useless_vec, items_after_test_module, field_reassign_with_default) and a missing required-features gate on storage_stats_test, so `cargo clippy --workspace --all-targets` reaches zero errors. Verified: cargo check --workspace and cargo clippy --workspace --all-targets -- -D warnings -D clippy::unwrap_used both pass with 0 errors; resp/client/kstd lib tests pass. RocksDB-dependent tests cannot run on this Windows host (pre-existing DLL entrypoint issue), to be confirmed in CI. Closes #396 Closes #397 Closes #398 Closes #399 Closes #400 Closes #401
1 parent cbc2895 commit 7d55fa8

32 files changed

Lines changed: 198 additions & 128 deletions

src/cmd/src/set.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ impl Cmd for SetCmd {
4848

4949
/// SET key value
5050
fn do_initial(&self, client: &Client) -> bool {
51-
// TODO: support xx, nx, ex, px
51+
// TODO(#400): support xx, nx, ex, px
5252
let argv = client.argv();
5353

5454
let key = argv[1].clone();

src/common/runtime/additional_unit_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ mod serialization_tests {
372372

373373
#[test]
374374
fn test_noop_storage_stats_collector() {
375-
let collector = NoopStorageStatsCollector::default();
375+
let collector = NoopStorageStatsCollector;
376376

377377
collector.record_read(3, 5);
378378
collector.record_write(7, 11);

src/common/runtime/baseline.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,17 @@ pub enum AttemptState {
8080
}
8181

8282
impl AttemptState {
83-
fn from_u8(value: u8) -> Self {
83+
fn from_u8(value: u8) -> Option<Self> {
8484
match value {
85-
0 => Self::Offered,
86-
1 => Self::ChannelQueued,
87-
2 => Self::BatchQueued,
88-
3 => Self::WaitingGate,
89-
4 => Self::Running,
90-
5 => Self::ExecutionFinished,
91-
6 => Self::ShutdownRejectedAfterAccept,
92-
7 => Self::Abandoned,
93-
_ => unreachable!("baseline attempt state is only written from AttemptState"),
85+
0 => Some(Self::Offered),
86+
1 => Some(Self::ChannelQueued),
87+
2 => Some(Self::BatchQueued),
88+
3 => Some(Self::WaitingGate),
89+
4 => Some(Self::Running),
90+
5 => Some(Self::ExecutionFinished),
91+
6 => Some(Self::ShutdownRejectedAfterAccept),
92+
7 => Some(Self::Abandoned),
93+
_ => None,
9494
}
9595
}
9696

@@ -322,6 +322,7 @@ impl BaselineAttempt {
322322

323323
pub fn state(&self) -> AttemptState {
324324
AttemptState::from_u8(self.inner.state.load(Ordering::Acquire))
325+
.unwrap_or(AttemptState::Abandoned)
325326
}
326327

327328
/// Irrevocably reject a physical attempt before the request channel accepts it.
@@ -1912,7 +1913,7 @@ mod tests {
19121913
source.transition(AttemptState::ChannelQueued).unwrap();
19131914

19141915
assert_eq!(
1915-
AttemptState::from_u8(target_inner.state.load(Ordering::Acquire)),
1916+
AttemptState::from_u8(target_inner.state.load(Ordering::Acquire)).unwrap(),
19161917
AttemptState::Abandoned
19171918
);
19181919
assert!(target_handle.failed());

src/common/runtime/storage_server.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,7 @@ impl BackgroundTaskManager {
10961096

10971097
// Record compaction started
10981098
if let Some(ref _tracker) = metrics_tracker {
1099-
// TODO: Fix type annotation issue
1099+
// TODO(#400): re-enable after type annotation fixed
11001100
// tracker.record_compaction_started().await;
11011101
}
11021102

@@ -1112,7 +1112,7 @@ impl BackgroundTaskManager {
11121112

11131113
// Record compaction completed
11141114
if let Some(ref _tracker) = metrics_tracker {
1115-
// TODO: Fix type annotation issue
1115+
// TODO(#400): re-enable after type annotation fixed
11161116
// tracker.record_compaction_completed(0, compaction_duration).await; // TODO: Get actual bytes compacted
11171117
}
11181118
}
@@ -1170,7 +1170,7 @@ impl BackgroundTaskManager {
11701170

11711171
// Record flush started
11721172
if let Some(ref _tracker) = metrics_tracker {
1173-
// TODO: Fix type annotation issue
1173+
// TODO(#400): re-enable after type annotation fixed
11741174
// tracker.record_flush_started().await;
11751175
}
11761176

@@ -1180,7 +1180,7 @@ impl BackgroundTaskManager {
11801180

11811181
// Record flush completed (simulated)
11821182
if let Some(ref _tracker) = metrics_tracker {
1183-
// TODO: Fix type annotation issue
1183+
// TODO(#400): re-enable after type annotation fixed
11841184
// tracker.record_flush_completed(0, Duration::from_millis(10)).await; // TODO: Get actual flush metrics
11851185
}
11861186
}
@@ -1236,7 +1236,7 @@ impl BackgroundTaskManager {
12361236

12371237
// Update metrics tracker if available
12381238
if let Some(ref _tracker) = metrics_tracker {
1239-
// TODO: Fix type annotation issue
1239+
// TODO(#400): re-enable after type annotation fixed
12401240
// tracker.update_rocksdb_metrics(
12411241
// rocksdb_stats.total_keys,
12421242
// rocksdb_stats.total_size_bytes,

src/executor/src/executor.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,18 @@ impl CmdExecutor {
100100
// send the work to the worker pool
101101
match self.work_tx.send(work).await {
102102
Ok(_) => {}
103-
Err(async_channel::SendError(_)) => {
103+
Err(async_channel::SendError(work)) => {
104104
// this should not happen, because the only case when all the workers
105105
// has been closed is when the executor is closed. and we've already
106-
// checked the cancellation_token.
107-
panic!("Failed to send work to worker; executor likely closed");
106+
// checked the cancellation_token. Degrade gracefully instead of panicking.
107+
error!("failed to send work to worker; executor likely closed");
108+
work.exec
109+
.client
110+
.set_reply(RespData::Error("ERR executor unavailable".into()));
108111
}
109112
}
110113

111-
// TODO: add a timeout for waiting
114+
// TODO(#400): add a timeout for waiting
112115
let _ = done_rx.await;
113116
}
114117

src/kstd/src/env.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ pub fn is_dir<P: AsRef<Path>>(path: P) -> io::Result<bool> {
2929

3030
/// Creates a directory and all its parent directories with the specified mode.
3131
/// This corresponds to the 'mkpath' functionality.
32-
/// TODO: remove allow dead code
32+
/// TODO(#400): remove allow dead code
3333
#[allow(dead_code)]
3434
pub fn mkdir_with_path<P: AsRef<Path>>(path: P, _mode: u32) -> io::Result<()> {
3535
// Use the fs::create_dir_all method to create the directory path.
@@ -46,7 +46,7 @@ pub fn mkdir_with_path<P: AsRef<Path>>(path: P, _mode: u32) -> io::Result<()> {
4646
Ok(())
4747
}
4848

49-
/// TODO: remove allow dead code
49+
/// TODO(#400): remove allow dead code
5050
#[allow(dead_code)]
5151
pub fn delete_dir<P: AsRef<Path>>(dirname: P) -> io::Result<()> {
5252
let path = dirname.as_ref();

src/kstd/src/status.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub struct Status {
2323
message: String,
2424
}
2525

26-
/// TODO: remove allow dead code.
26+
/// TODO(#400): remove allow dead code.
2727
#[allow(dead_code)]
2828
#[derive(Debug, PartialEq)]
2929
pub enum Code {
@@ -32,7 +32,7 @@ pub enum Code {
3232
Busy,
3333
}
3434

35-
/// TODO: remove allow dead code.
35+
/// TODO(#400): remove allow dead code.
3636
#[allow(dead_code)]
3737
impl Status {
3838
// Create a success status.

src/net/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub mod pool;
2828
pub mod storage_client;
2929
pub mod tcp;
3030

31-
// TODO: delete this module
31+
// TODO(#400): delete this module
3232
pub mod error;
3333
pub mod unix;
3434

src/net/src/optimized_handler.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,13 @@ impl OptimizedConnectionHandler {
201201
let error_response = RespData::Error(format!("ERR {}", e).into());
202202
let mut encoder = RespEncoder::new(RespVersion::RESP2);
203203
encoder.encode_resp_data(&error_response);
204-
let _ = client.write(encoder.get_response().as_ref()).await;
204+
if let Err(write_err) = client.write(encoder.get_response().as_ref()).await {
205+
warn!("failed to write error response to client: {write_err}; closing connection");
206+
if self.config.enable_buffer_pooling {
207+
self.buffer_manager.return_buffer(read_buffer).await;
208+
}
209+
return Ok(());
210+
}
205211
}
206212
}
207213
}

src/net/src/pipeline.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ pub struct CommandPipeline {
106106
storage: Arc<Storage>,
107107
cmd_table: Arc<CmdTable>,
108108
executor: Arc<CmdExecutor>,
109-
command_tx: mpsc::UnboundedSender<PipelineCommand>,
109+
command_tx: mpsc::Sender<PipelineCommand>,
110110
semaphore: Arc<Semaphore>,
111111
batch_counter: Arc<std::sync::atomic::AtomicU64>,
112112
}
@@ -118,9 +118,10 @@ impl CommandPipeline {
118118
cmd_table: Arc<CmdTable>,
119119
executor: Arc<CmdExecutor>,
120120
) -> Self {
121-
// TODO: Consider using bounded channel instead of unbounded to properly enforce queue_capacity
122-
// Currently using unbounded_channel means the command_queue_size config is not actually enforced
123-
let (command_tx, command_rx) = mpsc::unbounded_channel();
121+
// Bounded channel enforces backpressure: when the queue is full,
122+
// senders await instead of growing the queue unboundedly.
123+
// Guard against 0 (rendezvous) to avoid unintentional stalls.
124+
let (command_tx, command_rx) = mpsc::channel(config.command_queue_size.max(1));
124125
let semaphore = Arc::new(Semaphore::new(config.max_concurrent_pipelines));
125126

126127
let pipeline = Self {
@@ -154,9 +155,11 @@ impl CommandPipeline {
154155
response_tx,
155156
};
156157

157-
// Send command to pipeline
158+
// Send command to pipeline. On a full bounded channel this awaits
159+
// (backpressure) rather than growing the queue indefinitely.
158160
self.command_tx
159161
.send(command)
162+
.await
160163
.map_err(|_| PipelineError::ChannelClosed)?;
161164

162165
// Wait for response with timeout
@@ -167,7 +170,7 @@ impl CommandPipeline {
167170
}
168171

169172
/// Start the background batch processor
170-
fn start_batch_processor(&self, mut command_rx: mpsc::UnboundedReceiver<PipelineCommand>) {
173+
fn start_batch_processor(&self, mut command_rx: mpsc::Receiver<PipelineCommand>) {
171174
let config = self.config.clone();
172175
let storage = self.storage.clone();
173176
let cmd_table = self.cmd_table.clone();
@@ -368,11 +371,7 @@ impl CommandPipeline {
368371
PipelineStats {
369372
available_permits: self.semaphore.available_permits(),
370373
max_concurrent_pipelines: self.config.max_concurrent_pipelines,
371-
// Note: queue_capacity stat is currently misleading because the channel at line 121
372-
// is unbounded (mpsc::unbounded_channel). This returns command_queue_size from config,
373-
// but the actual channel has no capacity limit.
374-
// TODO: Consider using bounded channel (mpsc::channel) and passing config.command_queue_size
375-
// as the capacity to make this stat accurate.
374+
// The channel is bounded by config.command_queue_size.
376375
queue_capacity: self.config.command_queue_size,
377376
}
378377
}

0 commit comments

Comments
 (0)