Q: Any recommendations on how to process submit_sm asynchronously?
elvanja opened this issue · 3 comments
Hi,
For app I'm working on, due to business requirements, the :submit_sm
processing needs to perform some external services access which takes time. Therefore, we decided to try and handle those :submit_sm
's asynchronously. Currently it goes something like this:
defmodule OurSession do
use SMPPEX.Session
def handle_pdu(original_pdu, state) do
case SMPPEX.Pdu.command_name(original_pdu) do
:submit_sm ->
do_handle_submit_sm_async(original_pdu, state)
{:ok, [], state}
end
end
defp do_handle_submit_sm_async(original_pdu, state) do
Task.start(fn ->
# time intensive
data = fetch_data_from_external_service(original_pdu, state)
# use the external data to build response and new state
response_pdu = build_response_pdu(original_pdu, data)
new_state = build_new_state(original_pdu, state, data)
# set new state and reply to `:submit_sm`
:ok = SMPPEX.Session.call(state.session_pid, {:set_state, new_state})
:ok = SMPPEX.Session.send_pdu(state.session_pid, response_pdu)
end)
end
end
Unfortunately, due to GenServer calls in Session.call
and Session.send_pdu
, we must use this trick with Task.start
. I tried investigating various approached, including using Session.cast
to perform this, but none worked.
Do you have any suggestions on how to achieve asynchronous :submit_sm
processing, but in a more elegant manner?
Thank you for your time!
Hello!
Thank you for the feedback.
One could do something like this:
defmodule OurSession do
use SMPPEX.Session
def handle_pdu(original_pdu, state) do
case SMPPEX.Pdu.command_name(original_pdu) do
:submit_sm ->
Session.cast(self, {:submit_sm_async, original_pdu})
{:ok, [], state}
end
end
def handle_cast({:submit_sm_async, original_pdu}, state) do
data = fetch_data_from_external_service(original_pdu, state)
response_pdu = build_response_pdu(original_pdu, data)
new_state = build_new_state(original_pdu, state, data)
{:noreply, [response_pdu], new_state}
end
end
But it is quite dangerous, because Session process is the very process that handles the underlying SMPP & TCP connection, and while fetch_data_from_external_service
is working, the whole SMPP session does not handle anything.
That's why your approach is absolutely legit: to do the long work in a seperate process.
I can only suggest not make two consequent calls:
:ok = SMPPEX.Session.call(state.session_pid, {:set_state, new_state})
:ok = SMPPEX.Session.send_pdu(state.session_pid, response_pdu)
Just make one:
:ok = SMPPEX.Session.call(state.session_pid, {:send_response, response_pdu, new_state})
And return response from handler
def handle_call({:send_response, response_pdu, new_state}, _from, _state) do
{:reply, :ok, [response_pdu], new_state}
end
Also an advantage of the first approach (with handle_cast({:submit_sm_async, original_pdu}, state)
) is that the state
keeps being handled atomically.
Excellent, this is a push in the right direction definitely 👍
Thank you for your insight! 😄