perf: reduce presence update frequency and fix background tab throttling (#13726)
## Description Reduces the frequency of update_presence WebSocket calls from the live chat widget and fixes agents appearing offline when the dashboard is in a background tab. ## Fixes # (issue) https://github.com/chatwoot/chatwoot/issues/13720 ## Type of change - [ ] Bug fix (non-breaking change which fixes an issue) ## Checklist: - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules
This commit is contained in:
committed by
GitHub
parent
f4e6aa1bd2
commit
11826e2a21
@@ -6,7 +6,12 @@ const RECONNECT_INTERVAL = 1000;
|
|||||||
class BaseActionCableConnector {
|
class BaseActionCableConnector {
|
||||||
static isDisconnected = false;
|
static isDisconnected = false;
|
||||||
|
|
||||||
constructor(app, pubsubToken, websocketHost = '') {
|
constructor(
|
||||||
|
app,
|
||||||
|
pubsubToken,
|
||||||
|
websocketHost = '',
|
||||||
|
presenceInterval = PRESENCE_INTERVAL
|
||||||
|
) {
|
||||||
const websocketURL = websocketHost ? `${websocketHost}/cable` : undefined;
|
const websocketURL = websocketHost ? `${websocketHost}/cable` : undefined;
|
||||||
|
|
||||||
this.consumer = createConsumer(websocketURL);
|
this.consumer = createConsumer(websocketURL);
|
||||||
@@ -37,7 +42,7 @@ class BaseActionCableConnector {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.subscription.updatePresence();
|
this.subscription.updatePresence();
|
||||||
this.triggerPresenceInterval();
|
this.triggerPresenceInterval();
|
||||||
}, PRESENCE_INTERVAL);
|
}, presenceInterval);
|
||||||
};
|
};
|
||||||
this.triggerPresenceInterval();
|
this.triggerPresenceInterval();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,11 @@ const isMessageInActiveConversation = (getters, message) => {
|
|||||||
return activeConversationId && conversationId !== activeConversationId;
|
return activeConversationId && conversationId !== activeConversationId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const WIDGET_PRESENCE_INTERVAL = 60000;
|
||||||
|
|
||||||
class ActionCableConnector extends BaseActionCableConnector {
|
class ActionCableConnector extends BaseActionCableConnector {
|
||||||
constructor(app, pubsubToken) {
|
constructor(app, pubsubToken) {
|
||||||
super(app, pubsubToken);
|
super(app, pubsubToken, '', WIDGET_PRESENCE_INTERVAL);
|
||||||
this.events = {
|
this.events = {
|
||||||
'message.created': this.onMessageCreated,
|
'message.created': this.onMessageCreated,
|
||||||
'message.updated': this.onMessageUpdated,
|
'message.updated': this.onMessageUpdated,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ class Internal::RemoveStaleRedisKeysService
|
|||||||
|
|
||||||
def perform
|
def perform
|
||||||
Rails.logger.info "Removing redis stale keys for account #{@account_id}"
|
Rails.logger.info "Removing redis stale keys for account #{@account_id}"
|
||||||
range_start = (Time.zone.now - OnlineStatusTracker::PRESENCE_DURATION).to_i
|
range_start = (Time.zone.now - OnlineStatusTracker::CONTACT_PRESENCE_DURATION).to_i
|
||||||
# exclusive minimum score is specified by prefixing (
|
# exclusive minimum score is specified by prefixing (
|
||||||
# we are clearing old records because this could clogg up the sorted set
|
# we are clearing old records because this could clogg up the sorted set
|
||||||
::Redis::Alfred.zremrangebyscore(
|
::Redis::Alfred.zremrangebyscore(
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
class OnlineStatusTracker
|
class OnlineStatusTracker
|
||||||
# NOTE: You can customise the environment variable to keep your agents/contacts as online for longer
|
# NOTE: You can customise the environment variable to keep your agents/contacts as online for longer
|
||||||
PRESENCE_DURATION = ENV.fetch('PRESENCE_DURATION', 20).to_i.seconds
|
PRESENCE_DURATION = ENV.fetch('PRESENCE_DURATION', 20).to_i.seconds
|
||||||
|
# Widget pings every 60s, so contacts need a longer presence window
|
||||||
|
CONTACT_PRESENCE_DURATION = ENV.fetch('CONTACT_PRESENCE_DURATION', 90).to_i.seconds
|
||||||
|
|
||||||
# presence : sorted set with timestamp as the score & object id as value
|
# presence : sorted set with timestamp as the score & object id as value
|
||||||
|
|
||||||
@@ -11,7 +13,8 @@ class OnlineStatusTracker
|
|||||||
|
|
||||||
def self.get_presence(account_id, obj_type, obj_id)
|
def self.get_presence(account_id, obj_type, obj_id)
|
||||||
connected_time = ::Redis::Alfred.zscore(presence_key(account_id, obj_type), obj_id)
|
connected_time = ::Redis::Alfred.zscore(presence_key(account_id, obj_type), obj_id)
|
||||||
connected_time && connected_time > (Time.zone.now - PRESENCE_DURATION).to_i
|
duration = obj_type == 'Contact' ? CONTACT_PRESENCE_DURATION : PRESENCE_DURATION
|
||||||
|
connected_time && connected_time > (Time.zone.now - duration).to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.presence_key(account_id, type)
|
def self.presence_key(account_id, type)
|
||||||
@@ -39,7 +42,7 @@ class OnlineStatusTracker
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.get_available_contact_ids(account_id)
|
def self.get_available_contact_ids(account_id)
|
||||||
range_start = (Time.zone.now - PRESENCE_DURATION).to_i
|
range_start = (Time.zone.now - CONTACT_PRESENCE_DURATION).to_i
|
||||||
# exclusive minimum score is specified by prefixing (
|
# exclusive minimum score is specified by prefixing (
|
||||||
# we are clearing old records because this could clogg up the sorted set
|
# we are clearing old records because this could clogg up the sorted set
|
||||||
::Redis::Alfred.zremrangebyscore(presence_key(account_id, 'Contact'), '-inf', "(#{range_start}")
|
::Redis::Alfred.zremrangebyscore(presence_key(account_id, 'Contact'), '-inf', "(#{range_start}")
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ describe OnlineStatusTracker do
|
|||||||
described_class.update_presence(account.id, 'Contact', online_contact.id)
|
described_class.update_presence(account.id, 'Contact', online_contact.id)
|
||||||
# creating a stale record for offline contact presence
|
# creating a stale record for offline contact presence
|
||||||
Redis::Alfred.zadd(format(Redis::Alfred::ONLINE_PRESENCE_CONTACTS, account_id: account.id),
|
Redis::Alfred.zadd(format(Redis::Alfred::ONLINE_PRESENCE_CONTACTS, account_id: account.id),
|
||||||
(Time.zone.now - (OnlineStatusTracker::PRESENCE_DURATION + 20)).to_i, offline_contact.id)
|
(Time.zone.now - (OnlineStatusTracker::CONTACT_PRESENCE_DURATION + 20)).to_i, offline_contact.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns only the online contact ids with presence' do
|
it 'returns only the online contact ids with presence' do
|
||||||
|
|||||||
Reference in New Issue
Block a user