use super::retry_policy::at_least_once_options;
use super::stub::Stub;
use crate::RequestOptions;
use crate::model::{AcknowledgeRequest, ModifyAckDeadlineRequest};
use std::sync::Arc;
#[async_trait::async_trait]
pub(super) trait Leaser {
async fn ack(&self, ack_ids: Vec<String>);
async fn nack(&self, ack_ids: Vec<String>);
async fn extend(&self, ack_ids: Vec<String>);
}
pub(super) struct DefaultLeaser<T>
where
T: Stub,
{
inner: Arc<T>,
options: RequestOptions,
subscription: String,
ack_deadline_seconds: i32,
}
impl<T> Clone for DefaultLeaser<T>
where
T: Stub,
{
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
options: self.options.clone(),
subscription: self.subscription.clone(),
ack_deadline_seconds: self.ack_deadline_seconds,
}
}
}
impl<T> DefaultLeaser<T>
where
T: Stub,
{
pub(super) fn new(
inner: Arc<T>,
subscription: String,
ack_deadline_seconds: i32,
grpc_subchannel_count: usize,
) -> Self {
DefaultLeaser {
inner,
options: at_least_once_options(grpc_subchannel_count),
subscription,
ack_deadline_seconds,
}
}
}
#[async_trait::async_trait]
impl<T> Leaser for DefaultLeaser<T>
where
T: Stub,
{
async fn ack(&self, ack_ids: Vec<String>) {
let req = AcknowledgeRequest::new()
.set_subscription(self.subscription.clone())
.set_ack_ids(ack_ids);
let _ = self.inner.acknowledge(req, self.options.clone()).await;
}
async fn nack(&self, ack_ids: Vec<String>) {
let req = ModifyAckDeadlineRequest::new()
.set_subscription(self.subscription.clone())
.set_ack_ids(ack_ids)
.set_ack_deadline_seconds(0);
let _ = self
.inner
.modify_ack_deadline(req, self.options.clone())
.await;
}
async fn extend(&self, ack_ids: Vec<String>) {
let req = ModifyAckDeadlineRequest::new()
.set_subscription(self.subscription.clone())
.set_ack_ids(ack_ids)
.set_ack_deadline_seconds(self.ack_deadline_seconds);
let _ = self
.inner
.modify_ack_deadline(req, self.options.clone())
.await;
}
}
#[cfg(test)]
pub(super) mod tests {
use super::super::lease_state::tests::test_ids;
use super::super::retry_policy::tests::verify_policies;
use super::super::stub::tests::MockStub;
use super::*;
use crate::Response;
use std::sync::Arc;
use tokio::sync::Mutex;
mockall::mock! {
#[derive(Debug)]
pub(in super::super) Leaser {}
#[async_trait::async_trait]
impl Leaser for Leaser {
async fn ack(&self, ack_ids: Vec<String>);
async fn nack(&self, ack_ids: Vec<String>);
async fn extend(&self, ack_ids: Vec<String>);
}
}
#[async_trait::async_trait]
impl Leaser for Arc<MockLeaser> {
async fn ack(&self, ack_ids: Vec<String>) {
MockLeaser::ack(self, ack_ids).await
}
async fn nack(&self, ack_ids: Vec<String>) {
MockLeaser::nack(self, ack_ids).await
}
async fn extend(&self, ack_ids: Vec<String>) {
MockLeaser::extend(self, ack_ids).await
}
}
#[async_trait::async_trait]
impl Leaser for Arc<Mutex<MockLeaser>> {
async fn ack(&self, ack_ids: Vec<String>) {
self.lock().await.ack(ack_ids).await
}
async fn nack(&self, ack_ids: Vec<String>) {
self.lock().await.nack(ack_ids).await
}
async fn extend(&self, ack_ids: Vec<String>) {
self.lock().await.extend(ack_ids).await
}
}
#[test]
fn clone() {
let leaser = DefaultLeaser::new(
Arc::new(MockStub::new()),
"projects/my-project/subscriptions/my-subscription".to_string(),
10,
1_usize,
);
let clone = leaser.clone();
assert!(Arc::ptr_eq(&leaser.inner, &clone.inner));
assert_eq!(leaser.subscription, clone.subscription);
assert_eq!(leaser.ack_deadline_seconds, clone.ack_deadline_seconds);
}
#[tokio::test]
async fn ack() {
let mut mock = MockStub::new();
mock.expect_acknowledge().times(1).return_once(|r, o| {
assert_eq!(
r.subscription,
"projects/my-project/subscriptions/my-subscription"
);
assert_eq!(r.ack_ids, test_ids(0..10));
verify_policies(o, 16);
Ok(Response::from(()))
});
let leaser = DefaultLeaser::new(
Arc::new(mock),
"projects/my-project/subscriptions/my-subscription".to_string(),
10,
16_usize,
);
leaser.ack(test_ids(0..10)).await;
}
#[tokio::test]
async fn nack() {
let mut mock = MockStub::new();
mock.expect_modify_ack_deadline()
.times(1)
.return_once(|r, o| {
assert_eq!(r.ack_deadline_seconds, 0);
assert_eq!(
r.subscription,
"projects/my-project/subscriptions/my-subscription"
);
assert_eq!(r.ack_ids, test_ids(0..10));
verify_policies(o, 16);
Ok(Response::from(()))
});
let leaser = DefaultLeaser::new(
Arc::new(mock),
"projects/my-project/subscriptions/my-subscription".to_string(),
10,
16_usize,
);
leaser.nack(test_ids(0..10)).await;
}
#[tokio::test]
async fn extend() {
let mut mock = MockStub::new();
mock.expect_modify_ack_deadline()
.times(1)
.return_once(|r, o| {
assert_eq!(r.ack_deadline_seconds, 10);
assert_eq!(
r.subscription,
"projects/my-project/subscriptions/my-subscription"
);
assert_eq!(r.ack_ids, test_ids(0..10));
verify_policies(o, 16);
Ok(Response::from(()))
});
let leaser = DefaultLeaser::new(
Arc::new(mock),
"projects/my-project/subscriptions/my-subscription".to_string(),
10,
16_usize,
);
leaser.extend(test_ids(0..10)).await;
}
}