Software / code / prosody
Comparison
plugins/mod_smacks.lua @ 12051:c32ef09ab452
mod_smacks: Persist old counter values to storage
This allows clients that try to resume a session after a server restart
to at least know which of their pending outgoing stanzas were received
and which need to be re-sent.
This removes the limit on how many of those counters are kept, which
should be fixed eventually.
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Wed, 01 Dec 2021 16:41:10 +0100 |
| parent | 12050:7b87a1747eb2 |
| child | 12052:d17b8fcf11c7 |
comparison
equal
deleted
inserted
replaced
| 12050:7b87a1747eb2 | 12051:c32ef09ab452 |
|---|---|
| 43 local s2s_resend = module:get_option_boolean("smacks_s2s_resend", false); | 43 local s2s_resend = module:get_option_boolean("smacks_s2s_resend", false); |
| 44 local max_unacked_stanzas = module:get_option_number("smacks_max_unacked_stanzas", 0); | 44 local max_unacked_stanzas = module:get_option_number("smacks_max_unacked_stanzas", 0); |
| 45 local max_inactive_unacked_stanzas = module:get_option_number("smacks_max_inactive_unacked_stanzas", 256); | 45 local max_inactive_unacked_stanzas = module:get_option_number("smacks_max_inactive_unacked_stanzas", 256); |
| 46 local delayed_ack_timeout = module:get_option_number("smacks_max_ack_delay", 30); | 46 local delayed_ack_timeout = module:get_option_number("smacks_max_ack_delay", 30); |
| 47 local max_hibernated_sessions = module:get_option_number("smacks_max_hibernated_sessions", 10); | 47 local max_hibernated_sessions = module:get_option_number("smacks_max_hibernated_sessions", 10); |
| 48 local max_old_sessions = module:get_option_number("smacks_max_old_sessions", 10); | |
| 49 | 48 |
| 50 assert(max_hibernated_sessions > 0, "smacks_max_hibernated_sessions must be greater than 0"); | 49 assert(max_hibernated_sessions > 0, "smacks_max_hibernated_sessions must be greater than 0"); |
| 51 assert(max_old_sessions > 0, "smacks_max_old_sessions must be greater than 0"); | |
| 52 | 50 |
| 53 local c2s_sessions = module:shared("/*/c2s/sessions"); | 51 local c2s_sessions = module:shared("/*/c2s/sessions"); |
| 54 | 52 |
| 55 local function format_h(h) if h then return string.format("%d", h) end end | 53 local function format_h(h) if h then return string.format("%d", h) end end |
| 56 | 54 |
| 74 -- remove empty caches completely | 72 -- remove empty caches completely |
| 75 if stores[user]:count() == 0 then stores[user] = nil; end | 73 if stores[user]:count() == 0 then stores[user] = nil; end |
| 76 end; | 74 end; |
| 77 }; | 75 }; |
| 78 end | 76 end |
| 79 local old_session_registry = init_session_cache(max_old_sessions, nil); | 77 local old_session_registry = module:open_store("smacks_h", "map"); |
| 80 local session_registry = init_session_cache(max_hibernated_sessions, function(resumption_token, session) | 78 local session_registry = init_session_cache(max_hibernated_sessions, function(resumption_token, session) |
| 81 if session.destroyed then return true; end -- destroyed session can always be removed from cache | 79 if session.destroyed then return true; end -- destroyed session can always be removed from cache |
| 82 session.log("warn", "User has too much hibernated sessions, removing oldest session (token: %s)", resumption_token); | 80 session.log("warn", "User has too much hibernated sessions, removing oldest session (token: %s)", resumption_token); |
| 83 -- store old session's h values on force delete | 81 -- store old session's h values on force delete |
| 84 -- save only actual h value and username/host (for security) | 82 -- save only actual h value and username/host (for security) |
| 85 old_session_registry.set(session.username, resumption_token, { | 83 old_session_registry:set(session.username, resumption_token, { |
| 86 h = session.handled_stanza_count, | 84 h = session.handled_stanza_count, |
| 87 }); | 85 }); |
| 88 return true; -- allow session to be removed from full cache to make room for new one | 86 return true; -- allow session to be removed from full cache to make room for new one |
| 89 end); | 87 end); |
| 90 | 88 |
| 246 | 244 |
| 247 module:hook("pre-session-close", function(event) | 245 module:hook("pre-session-close", function(event) |
| 248 local session = event.session; | 246 local session = event.session; |
| 249 if session.resumption_token then | 247 if session.resumption_token then |
| 250 session_registry.set(session.username, session.resumption_token, nil); | 248 session_registry.set(session.username, session.resumption_token, nil); |
| 251 old_session_registry.set(session.username, session.resumption_token, nil); | 249 old_session_registry:set(session.username, session.resumption_token, nil); |
| 252 session.resumption_token = nil; | 250 session.resumption_token = nil; |
| 253 end | 251 end |
| 254 -- send out last ack as per revision 1.5.2 of XEP-0198 | 252 -- send out last ack as per revision 1.5.2 of XEP-0198 |
| 255 if session.smacks and session.conn and session.handled_stanza_count then | 253 if session.smacks and session.conn and session.handled_stanza_count then |
| 256 (session.sends2s or session.send)(st.stanza("a", { | 254 (session.sends2s or session.send)(st.stanza("a", { |
| 491 return resume_timeout-(current_time-timeout_start); -- time left to wait | 489 return resume_timeout-(current_time-timeout_start); -- time left to wait |
| 492 end | 490 end |
| 493 session.log("debug", "Destroying session for hibernating too long"); | 491 session.log("debug", "Destroying session for hibernating too long"); |
| 494 session_registry.set(session.username, session.resumption_token, nil); | 492 session_registry.set(session.username, session.resumption_token, nil); |
| 495 -- save only actual h value and username/host (for security) | 493 -- save only actual h value and username/host (for security) |
| 496 old_session_registry.set(session.username, session.resumption_token, { | 494 old_session_registry:set(session.username, session.resumption_token, { |
| 497 h = session.handled_stanza_count, | 495 h = session.handled_stanza_count, |
| 498 }); | 496 }); |
| 499 session.resumption_token = nil; | 497 session.resumption_token = nil; |
| 500 sessionmanager.destroy_session(session, "Hibernating too long"); | 498 sessionmanager.destroy_session(session, "Hibernating too long"); |
| 501 else | 499 else |
| 544 | 542 |
| 545 local id = stanza.attr.previd; | 543 local id = stanza.attr.previd; |
| 546 local original_session = session_registry.get(session.username, id); | 544 local original_session = session_registry.get(session.username, id); |
| 547 if not original_session then | 545 if not original_session then |
| 548 session.log("debug", "Tried to resume non-existent session with id %s", id); | 546 session.log("debug", "Tried to resume non-existent session with id %s", id); |
| 549 local old_session = old_session_registry.get(session.username, id); | 547 local old_session = old_session_registry:get(session.username, id); |
| 550 if old_session then | 548 if old_session then |
| 551 session.send(st.stanza("failed", { xmlns = xmlns_sm, h = format_h(old_session.h) }) | 549 session.send(st.stanza("failed", { xmlns = xmlns_sm, h = format_h(old_session.h) }) |
| 552 :tag("item-not-found", { xmlns = xmlns_errors }) | 550 :tag("item-not-found", { xmlns = xmlns_errors }) |
| 553 ); | 551 ); |
| 554 else | 552 else |