Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
daef6e0d17 | ||
|
|
8fe4ebde9b | ||
|
|
04287a0924 |
@@ -1,35 +0,0 @@
|
||||
# MutexApplicationJob serves as a base class for jobs that require distributed locking mechanisms.
|
||||
# It abstracts the locking logic using Redis and ensures that a block of code can be executed with
|
||||
# mutual exclusion.
|
||||
#
|
||||
# The primary mechanism provided is the `with_lock` method, which accepts a key format and associated
|
||||
# arguments. This method attempts to acquire a lock using the generated key, and if successful, it
|
||||
# executes the provided block of code. If the lock cannot be acquired, it raises a LockAcquisitionError.
|
||||
#
|
||||
# To use this class, inherit from MutexApplicationJob and make use of the `with_lock` method in the
|
||||
# `perform` method of the derived job class.
|
||||
#
|
||||
# Also see, retry mechanism here: https://edgeapi.rubyonrails.org/classes/ActiveJob/Exceptions/ClassMethods.html#method-i-retry_on
|
||||
#
|
||||
class MutexApplicationJob < ApplicationJob
|
||||
class LockAcquisitionError < StandardError; end
|
||||
|
||||
def with_lock(key_format, *args)
|
||||
lock_key = format(key_format, *args)
|
||||
lock_manager = Redis::LockManager.new
|
||||
|
||||
if lock_manager.locked?(lock_key)
|
||||
Rails.logger.error "[#{self.class.name}] Failed to acquire lock on attempt #{executions + 1}: #{lock_key}"
|
||||
raise LockAcquisitionError, "Failed to acquire lock for key: #{lock_key}"
|
||||
end
|
||||
|
||||
begin
|
||||
lock_manager.lock(lock_key)
|
||||
Rails.logger.info "[#{self.class.name}] Acquired lock for: #{lock_key}"
|
||||
yield
|
||||
ensure
|
||||
# Ensure that the lock is released even if there's an error in processing
|
||||
lock_manager.unlock(lock_key)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,10 +1,7 @@
|
||||
class SendOnSlackJob < MutexApplicationJob
|
||||
class SendOnSlackJob < ApplicationJob
|
||||
queue_as :medium
|
||||
retry_on LockAcquisitionError, wait: 3.seconds, attempts: 5
|
||||
|
||||
def perform(message, hook)
|
||||
with_lock(::Redis::Alfred::SLACK_MESSAGE_MUTEX, sender_id: message.sender_id, reference_id: hook.reference_id) do
|
||||
Integrations::Slack::SendOnSlackService.new(message: message, hook: hook).perform
|
||||
end
|
||||
Integrations::Slack::SendOnSlackService.new(message: message, hook: hook).perform
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
class Webhooks::FacebookEventsJob < ApplicationJob
|
||||
queue_as :default
|
||||
retry_on LockAcquisitionError, wait: 2.seconds, attempts: 5
|
||||
|
||||
def perform(message)
|
||||
response = ::Integrations::Facebook::MessageParser.new(message)
|
||||
|
||||
with_lock(::Redis::Alfred::FACEBOOK_MESSAGE_MUTEX, sender_id: response.sender_id, recipient_id: response.recipient_id) do
|
||||
::Integrations::Facebook::MessageCreator.new(response).perform
|
||||
end
|
||||
::Integrations::Facebook::MessageCreator.new(response).perform
|
||||
end
|
||||
end
|
||||
|
||||
@@ -28,7 +28,7 @@ class Integrations::Slack::ChannelBuilder
|
||||
channel_list = conversations_list.channels
|
||||
while conversations_list.response_metadata.next_cursor.present?
|
||||
conversations_list = slack_client.conversations_list(cursor: conversations_list.response_metadata.next_cursor)
|
||||
channel_list.concat(conversations_list.channel_list)
|
||||
channel_list.concat(conversations_list.channels)
|
||||
end
|
||||
channel_list
|
||||
end
|
||||
|
||||
@@ -36,7 +36,7 @@ class Integrations::Slack::IncomingMessageBuilder
|
||||
def should_process_event?
|
||||
return true if params[:type] != 'event_callback'
|
||||
|
||||
params[:event][:user].present? && params[:event][:subtype].blank?
|
||||
params[:event][:user].present? && (params[:event][:subtype].blank? || params[:event][:subtype] == 'file_share')
|
||||
end
|
||||
|
||||
def supported_event?
|
||||
|
||||
@@ -128,7 +128,8 @@ class Integrations::Slack::SendOnSlackService < Base::SendOnChannelService
|
||||
end
|
||||
|
||||
def update_reference_id
|
||||
return if conversation.identifier
|
||||
# If the conversation identifier is present and conversation identifier is not equal to slack message ts, then do nothing
|
||||
return if conversation.identifier && conversation.identifier == @slack_message['ts']
|
||||
|
||||
conversation.update!(identifier: @slack_message['ts'])
|
||||
end
|
||||
|
||||
@@ -33,9 +33,4 @@ module Redis::RedisKeys
|
||||
MESSAGE_SOURCE_KEY = 'MESSAGE_SOURCE_KEY::%<id>s'.freeze
|
||||
CUSTOM_FILTER_RECORDS_COUNT_KEY = 'CUSTOM_FILTER::%<account_id>d::%<user_id>d::%<filter_id>d'.freeze
|
||||
OPENAI_CONVERSATION_KEY = 'OPEN_AI_CONVERSATION_KEY::%<event_name>s::%<conversation_id>d::%<updated_at>d'.freeze
|
||||
|
||||
## Sempahores / Locks
|
||||
# We don't want to process messages from the same sender concurrently to prevent creating double conversations
|
||||
FACEBOOK_MESSAGE_MUTEX = 'FB_MESSAGE_CREATE_LOCK::%<sender_id>s::%<recipient_id>s'.freeze
|
||||
SLACK_MESSAGE_MUTEX = 'SLACK_MESSAGE_LOCK::%<sender_id>s::%<reference_id>s'.freeze
|
||||
end
|
||||
|
||||
@@ -97,6 +97,43 @@ describe Integrations::Slack::SendOnSlackService do
|
||||
expect(message.external_source_id_slack).to eq 'cw-origin-6789.12345'
|
||||
end
|
||||
|
||||
it 'send a message to a previously non-existent slack thread' do
|
||||
allow(slack_message).to receive(:[]).with('ts').and_return('1245.6789')
|
||||
|
||||
expect(slack_client).to receive(:chat_postMessage).with(
|
||||
channel: hook.reference_id,
|
||||
text: message.content,
|
||||
username: "#{message.sender.name} (Contact)",
|
||||
thread_ts: conversation.identifier,
|
||||
icon_url: anything,
|
||||
unfurl_links: true
|
||||
).and_return(slack_message)
|
||||
|
||||
builder.perform
|
||||
|
||||
expect(conversation.identifier).to eq '1245.6789'
|
||||
end
|
||||
|
||||
it 'send a message to a differnt channel in slack' do
|
||||
allow(slack_message).to receive(:[]).with('ts').and_return('1691652432.896169')
|
||||
|
||||
hook.update!(reference_id: 'C12345')
|
||||
|
||||
expect(slack_client).to receive(:chat_postMessage).with(
|
||||
channel: 'C12345',
|
||||
text: message.content,
|
||||
username: "#{message.sender.name} (Contact)",
|
||||
thread_ts: conversation.identifier,
|
||||
icon_url: anything,
|
||||
unfurl_links: true
|
||||
).and_return(slack_message)
|
||||
|
||||
builder.perform
|
||||
|
||||
expect(hook.reload.reference_id).to eq 'C12345'
|
||||
expect(conversation.identifier).to eq '1691652432.896169'
|
||||
end
|
||||
|
||||
it 'sent attachment on slack' do
|
||||
expect(slack_client).to receive(:chat_postMessage).with(
|
||||
channel: hook.reference_id,
|
||||
|
||||
Reference in New Issue
Block a user