question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

setStartWithLastReceived double delivers last acknowledged message

See original GitHub issue

Hi, I am setting up a subscription with queue groups and durable name, and I am using the setStartWithLastReceived option. Receiving messages works just fine, but when I restart my consumer, I get the last message I had already received in the previous session. I know thats what setStartWithLastReceived sounds like, but I had thought/hoped that we would start off with any new un-acked messages, rather than last one I had already processed. So, essentially, I have double delivery of the last message during consumer restarts. Is this expected? Should I be using some other option?

Here is the code snippet:

const opts =  this.client.subscriptionOptions().setStartWithLastReceived().setDurableName("durable-name");
const subscription = this.client.subscribe(topic, queue, opts);

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
ujwal-setlurcommented, Dec 3, 2019

OK, I don’t see this problem with this code:

stan-pub.js:

const STAN = require('node-nats-streaming');

const argv = require('minimist')(process.argv.slice(2));
const cluster_id = argv.c || "test-cluster";
const client_id = argv.i || "node-stan-pub";
const server = argv.s || 'nats://localhost:4222';
console.log("server: " + server);
const queueGroup = argv.q || "";
const topic = argv.t || "";
const message = argv._[0];

if (!topic || !message) {
                    usage();
}

function usage() {
                    console.log('stan-sub [-c clusterId] [-i clientId] [-s server] [-q queueGroup] -t <topic> <message>');
                    process.exit();
}


const stan = STAN.connect(cluster_id, client_id, server);
stan.on('connect', function() {
                    console.log("STAN connected!");
                    stan.publish(topic, message, null);
});

stan.on('error', function(reason) {
                    console.log(reason);
});
node stan-pub.js -s nats://nats-streaming:4222 -t foo bar
server: nats://nats-streaming:4222
STAN connected!

stan-sub.js:

const STAN = require('node-nats-streaming');

const argv = require('minimist')(process.argv.slice(2));
const cluster_id = argv.c || "test-cluster";
const client_id = argv.i || "node-stan-sub";
const server = argv.s || 'nats://localhost:4222';
console.log("server: " + server);
const durable = argv.d || undefined;
const queueGroup = argv.q || "";
const subject = argv._[0];

if (!subject) {
            usage();
}

function usage() {
            console.log('stan-sub [-d durable] [-k startSeq] [-c clusterId] [-i clientId] [-s server] [-q queueGroup] <subject>');
            process.exit();
}


const stan = STAN.connect(cluster_id, client_id, server);
stan.on('connect', function() {
            console.log("STAN connected!");
            const opts = stan.subscriptionOptions();
            opts.setStartWithLastReceived();
            if(durable) {
                            opts.setDurableName(durable);
                        }

            const subscription = stan.subscribe(subject, queueGroup, opts);
            subscription.on('error', (err) => {
                            console.log(`subscription for ${subject} raised an error: ${err}`);
                        });
            subscription.on('unsubscribed', () => {
                            console.log(`unsubscribed to ${subject}`);
                        });
            subscription.on('ready', () => {
                            console.log(`subscribed to ${subscription.subject}`);
                        });
            subscription.on('message', (msg) => {
                            console.log(msg.getSubject(), `[${msg.getSequence()}]`, msg.getData());
                        });
});

stan.on('error', function(reason) {
            console.log(reason);
});
node stan-sub.js -s nats://nats-streaming:4222 -d dur -q myqueue foo
server: nats://nats-streaming:4222
STAN connected!
subscribed to foo
foo [3] bar

On next restart of subscriber:

node stan-sub.js -s nats://nats-streaming:4222 -c byte-cluster -i id4 -d dur -q myqueue foo
server: nats://nats-streaming:4222
STAN connected!
subscribed to foo

Looks like there is a bug in the moleculer stan transporter code.

0reactions
aricartcommented, Dec 4, 2019

There are quite a few concepts that you should read about here:

https://docs.nats.io/developing-with-nats-streaming/receiving

In a nutshell:

For regular subscriptions, the server only maintains state until the client closes, or unsubscribes - this allows the server to remember to resume a channel when it crashes and is using a persistent store. From the client’s perspective, close and unsusbscribe are equivalent.

When you have a durable subscription the server remembers the messages you have not acknowledged and resends them to you until you unsubscribe. If your client closes(), it still remembers your position in the stream. It is possible that the server may send you a message again. You can check this with isRedelivered(), as the server only knows you got the message if it receives an acknowledgment from the client.

When you unsubscribe(), the server stops sending you messages from that channel, and drop the state you had on the channel.

Queue subscriptions are similar to a non-queue durable with the exception that unsubscribe() only drops the channel’s state for the queue when all clients unsubscribe. This allows the queue to grow or shrink while still maintaining state on the channel.

Read more comments on GitHub >

github_iconTop Results From Across the Web

node-nats-streaming/README.md - UNPKG
NATS Streaming offers At-Least-Once delivery semantics, meaning that once a message has been delivered to an eligible subscriber, if an acknowledgement is not ......
Read more >
JavaScript nuid next Examples
it('subscribe after 500ms on last received', (done) => { const stan ... getData()).equal('first', 'second message was not the one expected'); stan.close(); ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found