Skip to content

Commit 1f246c7

Browse files
authored
fix(server): release lock before async save to prevent deadlock (#1567)
- In `consumer_offsets.rs`, clone the consumer offset's path and drop the mutable guard before awaiting the async save. - Refactor `save_consumer_offset` to accept a new offset value and path instead of a ConsumerOffset reference. - Update error messages and trace logs accordingly. - Bump version in Cargo.toml from 0.4.210 to 0.4.211.
1 parent dc36d85 commit 1f246c7

File tree

7 files changed

+23
-28
lines changed

7 files changed

+23
-28
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

integration/tests/streaming/consumer_offset.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@ async fn assert_persisted_offset(
4242
consumer_offset: &ConsumerOffset,
4343
expected_offsets_count: u32,
4444
) {
45-
storage.save_consumer_offset(consumer_offset).await.unwrap();
45+
storage
46+
.save_consumer_offset(consumer_offset.offset, &consumer_offset.path)
47+
.await
48+
.unwrap();
4649
let consumer_offsets = storage
4750
.load_consumer_offsets(consumer_offset.kind, path)
4851
.await
@@ -51,11 +54,7 @@ async fn assert_persisted_offset(
5154
assert_eq!(consumer_offsets.len(), expected_offsets_count);
5255
let loaded_consumer_offset = consumer_offsets.get(expected_offsets_count - 1).unwrap();
5356

54-
// TODO(hubcio): This is a workaround: sometimes offset is 4, sometimes 5
55-
let offset_ok = loaded_consumer_offset.offset == consumer_offset.offset
56-
|| loaded_consumer_offset.offset == consumer_offset.offset + 1
57-
|| loaded_consumer_offset.offset == consumer_offset.offset - 1;
58-
assert!(offset_ok);
57+
assert!(loaded_consumer_offset.offset == consumer_offset.offset);
5958

6059
assert_eq!(loaded_consumer_offset.kind, consumer_offset.kind);
6160
assert_eq!(

server/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "server"
3-
version = "0.4.210"
3+
version = "0.4.211"
44
edition = "2021"
55
build = "src/build.rs"
66
license = "Apache-2.0"

server/src/streaming/partitions/consumer_offsets.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,15 @@ impl Partition {
7878
let consumer_offsets = self.get_consumer_offsets(kind);
7979
if let Some(mut consumer_offset) = consumer_offsets.get_mut(&consumer_id) {
8080
consumer_offset.offset = offset;
81+
let path = consumer_offset.path.clone();
82+
drop(consumer_offset);
8183
self.storage
8284
.partition
83-
.save_consumer_offset(&consumer_offset)
85+
.save_consumer_offset(offset, &path)
8486
.await
8587
.with_error_context(|error| {
8688
format!(
87-
"{COMPONENT} (error: {error}) - failed to save consumer offset, consumer ID: {}, offset: {}",
88-
consumer_id, offset
89+
"{COMPONENT} (error: {error}) - failed to save consumer offset, consumer ID: {consumer_id}, offset: {offset}, path: {path}",
8990
)
9091
})?;
9192
return Ok(());
@@ -98,7 +99,7 @@ impl Partition {
9899
let consumer_offset = ConsumerOffset::new(kind, consumer_id, offset, path);
99100
self.storage
100101
.partition
101-
.save_consumer_offset(&consumer_offset)
102+
.save_consumer_offset(offset, &consumer_offset.path)
102103
.await
103104
.with_error_context(|error| {
104105
format!(

server/src/streaming/partitions/partition.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub struct ConsumerOffset {
5454
pub kind: ConsumerKind,
5555
pub consumer_id: u32,
5656
pub offset: u64,
57-
pub path: String,
57+
pub path: Arc<String>,
5858
}
5959

6060
impl ConsumerOffset {
@@ -63,7 +63,7 @@ impl ConsumerOffset {
6363
kind,
6464
consumer_id,
6565
offset,
66-
path: format!("{path}/{consumer_id}"),
66+
path: Arc::new(format!("{path}/{consumer_id}")),
6767
}
6868
}
6969
}

server/src/streaming/partitions/storage.rs

+6-12
Original file line numberDiff line numberDiff line change
@@ -335,21 +335,15 @@ impl PartitionStorage for FilePartitionStorage {
335335
Ok(())
336336
}
337337

338-
async fn save_consumer_offset(&self, offset: &ConsumerOffset) -> Result<(), IggyError> {
338+
async fn save_consumer_offset(&self, offset: u64, path: &str) -> Result<(), IggyError> {
339339
self.persister
340-
.overwrite(&offset.path, &offset.offset.to_le_bytes())
340+
.overwrite(path, &offset.to_le_bytes())
341341
.await
342342
.with_error_context(|error| format!(
343-
"{COMPONENT} (error: {error}) - failed to overwrite consumer offset with value: {}, kind: {}, consumer ID: {}, path: {}",
344-
offset.offset, offset.kind, offset.consumer_id, offset.path,
343+
"{COMPONENT} (error: {error}) - failed to overwrite consumer offset with value: {}, path: {}",
344+
offset, path,
345345
))?;
346-
trace!(
347-
"Stored consumer offset value: {} for {} with ID: {}, path: {}",
348-
offset.offset,
349-
offset.kind,
350-
offset.consumer_id,
351-
offset.path
352-
);
346+
trace!("Stored consumer offset value: {}, path: {}", offset, path);
353347
Ok(())
354348
}
355349

@@ -390,7 +384,7 @@ impl PartitionStorage for FilePartitionStorage {
390384
continue;
391385
}
392386

393-
let path = path.unwrap().to_string();
387+
let path = Arc::new(path.unwrap().to_string());
394388
let consumer_id = consumer_id.unwrap();
395389
let mut file = file::open(&path)
396390
.await

server/src/streaming/storage.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ pub trait PartitionStorage: Send {
105105
fn delete(&self, partition: &Partition) -> impl Future<Output = Result<(), IggyError>> + Send;
106106
fn save_consumer_offset(
107107
&self,
108-
offset: &ConsumerOffset,
108+
offset: u64,
109+
path: &str,
109110
) -> impl Future<Output = Result<(), IggyError>> + Send;
110111
fn load_consumer_offsets(
111112
&self,
@@ -177,7 +178,7 @@ impl PartitionStorageKind {
177178
-> Result<(), IggyError>;
178179
async fn save(&self, partition: &mut Partition) -> Result<(), IggyError>;
179180
async fn delete(&self, partition: &Partition) -> Result<(), IggyError>;
180-
async fn save_consumer_offset(&self, offset: &ConsumerOffset) -> Result<(), IggyError>;
181+
async fn save_consumer_offset(&self, offset: u64, path: &str) -> Result<(), IggyError>;
181182
async fn load_consumer_offsets(
182183
&self,
183184
kind: ConsumerKind,

0 commit comments

Comments
 (0)