Skip to content

Commit 0e6b01b

Browse files
committed
Updated JetstreamMessage controls.
1 parent d1993f8 commit 0e6b01b

5 files changed

Lines changed: 126 additions & 23 deletions

File tree

‎python/natsrpy/_natsrpy_rs/js/__init__.pyi‎

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from datetime import timedelta
12
from typing import Any
23

34
from .managers import KVManager, StreamsManager
@@ -26,4 +27,57 @@ class JetStreamMessage:
2627
def payload(self) -> bytes: ...
2728
@property
2829
def headers(self) -> dict[str, Any]: ...
29-
async def ack(self) -> None: ...
30+
async def ack(self, double: bool = False) -> None:
31+
"""
32+
Acknowledge that a message was handled.
33+
34+
:param double: whether to wait for server response, defaults to False
35+
"""
36+
37+
async def nack(
38+
self,
39+
delay: float | timedelta | None = None,
40+
double: bool = False,
41+
) -> None:
42+
"""
43+
Negative acknowledgement.
44+
45+
Signals that the message will not be processed now
46+
and processing can move onto the next message, NAK'd
47+
message will be retried.
48+
49+
:param duration: time, defaults to None
50+
:param double: whether to wait for server response, defaults to False
51+
"""
52+
53+
async def progress(self, double: bool = False) -> None:
54+
"""
55+
Progress acknowledgement.
56+
57+
Singnals that the mesasge is being handled right now.
58+
Sending this request before the AckWait will extend wait period
59+
before redelivering a message.
60+
61+
:param double: whether to wait for server response, defaults to False
62+
"""
63+
64+
async def next(self, double: bool = False) -> None:
65+
"""
66+
Next acknowledgement.
67+
68+
Only applies to pull consumers!
69+
Acknowledges message processing and instructs server to send
70+
delivery of the next message to the reply subject.
71+
72+
:param double: whether to wait for server response, defaults to False
73+
"""
74+
75+
async def term(self, double: bool = False) -> None:
76+
"""
77+
Term acknowledgement.
78+
79+
Instructs server to stop redelivering message.
80+
Useful to stop redelivering a message after multiple NACKs.
81+
82+
:param double: whether to wait for server response, defaults to False
83+
"""

‎src/js/consumers/pull/consumer.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use tokio::sync::RwLock;
66

77
use crate::{
88
exceptions::rust_err::NatsrpyResult,
9-
utils::{futures::natsrpy_future_with_timeout, py_types::TimeoutValue},
9+
utils::{futures::natsrpy_future_with_timeout, py_types::TimeValue},
1010
};
1111

1212
type NatsPullConsumer =
@@ -51,7 +51,7 @@ impl PullConsumer {
5151
expires: Option<Duration>,
5252
min_pending: Option<usize>,
5353
min_ack_pending: Option<usize>,
54-
timeout: Option<TimeoutValue>,
54+
timeout: Option<TimeValue>,
5555
) -> NatsrpyResult<Bound<'py, PyAny>> {
5656
let ctx = self.consumer.clone();
5757

‎src/js/message.rs‎

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use tokio::sync::RwLock;
44

55
use crate::{
66
exceptions::rust_err::NatsrpyResult,
7-
utils::{headers::NatsrpyHeadermapExt, natsrpy_future},
7+
utils::{headers::NatsrpyHeadermapExt, natsrpy_future, py_types::TimeValue},
88
};
99

1010
#[pyo3::pyclass]
@@ -25,6 +25,25 @@ impl From<async_nats::jetstream::Message> for JetStreamMessage {
2525
}
2626
}
2727

28+
impl JetStreamMessage {
29+
pub fn inner_ack<'py>(
30+
&self,
31+
py: Python<'py>,
32+
kind: async_nats::jetstream::message::AckKind,
33+
double: bool,
34+
) -> NatsrpyResult<Bound<'py, PyAny>> {
35+
let acker_guard = self.acker.clone();
36+
natsrpy_future(py, async move {
37+
if double {
38+
acker_guard.read().await.double_ack_with(kind).await?;
39+
} else {
40+
acker_guard.read().await.ack_with(kind).await?;
41+
}
42+
Ok(())
43+
})
44+
}
45+
}
46+
2847
#[pyo3::pymethods]
2948
impl JetStreamMessage {
3049
#[getter]
@@ -51,11 +70,41 @@ impl JetStreamMessage {
5170
}
5271
}
5372

54-
pub fn ack<'py>(&self, py: Python<'py>) -> NatsrpyResult<Bound<'py, PyAny>> {
55-
let acker_guard = self.acker.clone();
56-
natsrpy_future(py, async move {
57-
acker_guard.read().await.ack().await?;
58-
Ok(())
59-
})
73+
#[pyo3(signature=(double=false))]
74+
pub fn ack<'py>(&self, py: Python<'py>, double: bool) -> NatsrpyResult<Bound<'py, PyAny>> {
75+
self.inner_ack(py, async_nats::jetstream::message::AckKind::Ack, double)
76+
}
77+
78+
#[pyo3(signature=(delay=None, double=false))]
79+
pub fn nack<'py>(
80+
&self,
81+
py: Python<'py>,
82+
delay: Option<TimeValue>,
83+
double: bool,
84+
) -> NatsrpyResult<Bound<'py, PyAny>> {
85+
self.inner_ack(
86+
py,
87+
async_nats::jetstream::message::AckKind::Nak(delay.map(Into::into)),
88+
double,
89+
)
90+
}
91+
92+
#[pyo3(signature=(double=false))]
93+
pub fn progress<'py>(&self, py: Python<'py>, double: bool) -> NatsrpyResult<Bound<'py, PyAny>> {
94+
self.inner_ack(
95+
py,
96+
async_nats::jetstream::message::AckKind::Progress,
97+
double,
98+
)
99+
}
100+
101+
#[pyo3(signature=(double=false))]
102+
pub fn next<'py>(&self, py: Python<'py>, double: bool) -> NatsrpyResult<Bound<'py, PyAny>> {
103+
self.inner_ack(py, async_nats::jetstream::message::AckKind::Next, double)
104+
}
105+
106+
#[pyo3(signature=(double=false))]
107+
pub fn term<'py>(&self, py: Python<'py>, double: bool) -> NatsrpyResult<Bound<'py, PyAny>> {
108+
self.inner_ack(py, async_nats::jetstream::message::AckKind::Term, double)
60109
}
61110
}

‎src/nats_cls.rs‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::{
1313
futures::natsrpy_future_with_timeout,
1414
headers::NatsrpyHeadermapExt,
1515
natsrpy_future,
16-
py_types::{SendableValue, TimeoutValue},
16+
py_types::{SendableValue, TimeValue},
1717
},
1818
};
1919

@@ -28,8 +28,8 @@ pub struct NatsCls {
2828
read_buffer_capacity: u16,
2929
sender_capacity: usize,
3030
max_reconnects: Option<usize>,
31-
connection_timeout: TimeoutValue,
32-
request_timeout: Option<TimeoutValue>,
31+
connection_timeout: TimeValue,
32+
request_timeout: Option<TimeValue>,
3333
}
3434

3535
#[pyo3::pymethods]
@@ -45,8 +45,8 @@ impl NatsCls {
4545
read_buffer_capacity=65535,
4646
sender_capacity=128,
4747
max_reconnects=None,
48-
connection_timeout=TimeoutValue::FloatSecs(5.0),
49-
request_timeout=TimeoutValue::FloatSecs(10.0),
48+
connection_timeout=TimeValue::FloatSecs(5.0),
49+
request_timeout=TimeValue::FloatSecs(10.0),
5050
))]
5151
fn __new__(
5252
addrs: Vec<String>,
@@ -57,8 +57,8 @@ impl NatsCls {
5757
read_buffer_capacity: u16,
5858
sender_capacity: usize,
5959
max_reconnects: Option<usize>,
60-
connection_timeout: TimeoutValue,
61-
request_timeout: Option<TimeoutValue>,
60+
connection_timeout: TimeValue,
61+
request_timeout: Option<TimeValue>,
6262
) -> Self {
6363
Self {
6464
nats_session: Arc::new(RwLock::new(None)),

‎src/utils/py_types.rs‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,21 @@ impl From<SendableValue> for bytes::Bytes {
4444
}
4545

4646
#[derive(Clone, Debug, Copy, PartialEq, PartialOrd)]
47-
pub enum TimeoutValue {
47+
pub enum TimeValue {
4848
Duration(Duration),
4949
FloatSecs(f32),
5050
}
5151

52-
impl From<TimeoutValue> for Duration {
53-
fn from(value: TimeoutValue) -> Self {
52+
impl From<TimeValue> for Duration {
53+
fn from(value: TimeValue) -> Self {
5454
match value {
55-
TimeoutValue::Duration(duration) => duration,
56-
TimeoutValue::FloatSecs(fsecs) => Self::from_secs_f32(fsecs),
55+
TimeValue::Duration(duration) => duration,
56+
TimeValue::FloatSecs(fsecs) => Self::from_secs_f32(fsecs),
5757
}
5858
}
5959
}
6060

61-
impl<'py> FromPyObject<'_, 'py> for TimeoutValue {
61+
impl<'py> FromPyObject<'_, 'py> for TimeValue {
6262
type Error = NatsrpyError;
6363

6464
fn extract(obj: pyo3::Borrowed<'_, 'py, pyo3::PyAny>) -> Result<Self, Self::Error> {

0 commit comments

Comments
 (0)