-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[CHANGED] MQTT s.clear() do not wait for JS responses when disconnecting the session #5575
Changes from 2 commits
ddda72d
00efd84
25490e1
8c9faa7
539b79f
59db092
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1000,7 +1000,7 @@ func (s *Server) mqttHandleClosedClient(c *client) { | |
|
||
// This needs to be done outside of any lock. | ||
if doClean { | ||
if err := sess.clear(); err != nil { | ||
if err := sess.clear(true); err != nil { | ||
c.Errorf(err.Error()) | ||
} | ||
} | ||
|
@@ -1475,7 +1475,7 @@ func (s *Server) mqttCreateAccountSessionManager(acc *Account, quitCh chan struc | |
// Opportunistically delete the old (legacy) consumer, from v2.10.10 and | ||
// before. Ignore any errors that might arise. | ||
rmLegacyDurName := mqttRetainedMsgsStreamName + "_" + jsa.id | ||
jsa.deleteConsumer(mqttRetainedMsgsStreamName, rmLegacyDurName) | ||
jsa.deleteConsumer(mqttRetainedMsgsStreamName, rmLegacyDurName, true) | ||
|
||
// Create a new, uniquely names consumer for retained messages for this | ||
// server. The prior one will expire eventually. | ||
|
@@ -1622,6 +1622,10 @@ func (jsa *mqttJSA) newRequestExMulti(kind, subject, cidHash string, hdrs []int, | |
r2i[reply] = i | ||
} | ||
|
||
if timeout == 0 { | ||
levb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return nil, nil | ||
} | ||
|
||
// Wait for all responses to come back, or for the timeout to expire. We | ||
// don't want to use time.After() which causes memory growth because the | ||
// timer can't be stopped and will need to expire to then be garbage | ||
|
@@ -1701,9 +1705,17 @@ func (jsa *mqttJSA) createDurableConsumer(cfg *CreateConsumerRequest) (*JSApiCon | |
return ccr, ccr.ToError() | ||
} | ||
|
||
func (jsa *mqttJSA) deleteConsumer(streamName, consName string) (*JSApiConsumerDeleteResponse, error) { | ||
// if noWait is specified, does not wait for the JS response, returns nil | ||
func (jsa *mqttJSA) deleteConsumer(streamName, consName string, noWait bool) (*JSApiConsumerDeleteResponse, error) { | ||
subj := fmt.Sprintf(JSApiConsumerDeleteT, streamName, consName) | ||
cdri, err := jsa.newRequest(mqttJSAConsumerDel, subj, 0, nil) | ||
|
||
timeout := mqttJSAPITimeout | ||
levb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if noWait { | ||
timeout = 0 | ||
} | ||
|
||
// timeout == 0 signals that we don't want to wait for the response. | ||
cdri, err := jsa.newRequestEx(mqttJSAConsumerDel, subj, _EMPTY_, -1, nil, timeout) | ||
levb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
return nil, err | ||
} | ||
|
@@ -3178,7 +3190,7 @@ func (sess *mqttSession) save() error { | |
// | ||
// Runs from the client's readLoop. | ||
// Lock not held on entry, but session is in the locked map. | ||
func (sess *mqttSession) clear() error { | ||
func (sess *mqttSession) clear(noWait bool) error { | ||
var durs []string | ||
var pubRelDur string | ||
|
||
|
@@ -3206,19 +3218,19 @@ func (sess *mqttSession) clear() error { | |
sess.mu.Unlock() | ||
|
||
for _, dur := range durs { | ||
if _, err := sess.jsa.deleteConsumer(mqttStreamName, dur); isErrorOtherThan(err, JSConsumerNotFoundErr) { | ||
if _, err := sess.jsa.deleteConsumer(mqttStreamName, dur, noWait); isErrorOtherThan(err, JSConsumerNotFoundErr) { | ||
return fmt.Errorf("unable to delete consumer %q for session %q: %v", dur, sess.id, err) | ||
} | ||
} | ||
if pubRelDur != _EMPTY_ { | ||
_, err := sess.jsa.deleteConsumer(mqttOutStreamName, pubRelDur) | ||
_, err := sess.jsa.deleteConsumer(mqttOutStreamName, pubRelDur, noWait) | ||
if isErrorOtherThan(err, JSConsumerNotFoundErr) { | ||
return fmt.Errorf("unable to delete consumer %q for session %q: %v", pubRelDur, sess.id, err) | ||
} | ||
} | ||
|
||
if seq > 0 { | ||
err := sess.jsa.deleteMsg(mqttSessStreamName, seq, true) | ||
err := sess.jsa.deleteMsg(mqttSessStreamName, seq, !noWait) | ||
// Ignore the various errors indicating that the message (or sequence) | ||
// is already deleted, can happen in a cluster. | ||
if isErrorOtherThan(err, JSSequenceNotFoundErrF) { | ||
|
@@ -3484,7 +3496,7 @@ func (sess *mqttSession) untrackPubRel(pi uint16) (jsAckSubject string) { | |
func (sess *mqttSession) deleteConsumer(cc *ConsumerConfig) { | ||
sess.mu.Lock() | ||
sess.tmaxack -= cc.MaxAckPending | ||
sess.jsa.sendq.push(&mqttJSPubMsg{subj: sess.jsa.prefixDomain(fmt.Sprintf(JSApiConsumerDeleteT, mqttStreamName, cc.Durable))}) | ||
sess.jsa.deleteConsumer(mqttStreamName, cc.Durable, true) | ||
levb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
sess.mu.Unlock() | ||
} | ||
|
||
|
@@ -3823,7 +3835,7 @@ CHECK: | |
// This Session lasts as long as the Network Connection. State data | ||
// associated with this Session MUST NOT be reused in any subsequent | ||
// Session. | ||
if err := es.clear(); err != nil { | ||
if err := es.clear(false); err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At least here we make sure that the delete is complete, which I think is the right thing to do. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right. I understand why checking the success is important here, thus preserved as before. |
||
asm.removeSession(es, true) | ||
return err | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be invoked for "clean" sessions, which means sessions which state should be discarded and not reused in any subsequent session. Could there be a situation where a create/close/create causes an issue since some assets may not have been completely removed from the JS server?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't do anything with the error other than logging it anyway, so the logic doesn't change. Also, if the session is re-created as clean, we'd clean out its state on CONNECT. If it is re-created as persistent... 0/5 we could wipe out and re-create the consumers if there was a session message; but if we failed to delete the session message on the "clean" DISCONNECT... well, it still would not change the existing logic, would it?