3

I am trying to create a slack application in Perl with mojolicious and I am having the following use case:

Slack sends a request to my API from a slash command and needs a response in a 3 seconds timeframe. However, Slack also gives me the opportunity to send up to 5 more responses in a 30 minute timeframe but still needs an initial response in 3 seconds (it just sends a "late_response_url" in the initial call back so that I could POST something to that url later on). In my case I would like to send an initial response to slack to inform the user that the operation is "running" and after a while send the actual outcome of my slow function to Slack.

Currently, I can do this by spawning a second process using fork() and using one process to respond imidiately as Slack dictates and the second to do the rest of the work and respond later on.

I am trying to do this with Mojolicious' subprocesses to avoid using fork(). However I can't find a way to get this to work....

a sample code of what I am already doing with fork is like this:

sub withpath
{
     my $c = shift;

     my $user   = $c->param('user_name');

     my $response_body = {
                response_type => "ephemeral",
                text          => "Running for $user:",
                attachments   => [
                     { text => 'analyze' },
                 ],
             };
     my $pid = fork();
     if($pid != 0){
         $c->render( json => $response_body );
     }else{
         $output = do_time_consuming_things()
         $response_body = {
             response_type => "in-channel",
             text          => "Result for $user:",
             attachments   => [
                { text => $output },
             ],
         };

         my $ua = Mojo::UserAgent->new;
         my $tx = $ua->post(
               $response_url,
               { Accept => '*/*' },
               json => $response_body,
               );
        if( my $res = $tx->success )
        {
                print "\n success \n";
        }
        else
        {
                my $err = $tx->error;
                print "$err->{code} response: $err->{message}\n" if $err->{code};
                print "Connection error: $err->{message}\n";
        }
    }
}

So the problem is that no matter how I tried I couldn't replicate the exact same code with Mojolicious' subproccesses. Any ideas?

Thanks in advance!

Erik Kalkoken
  • 30,467
  • 8
  • 79
  • 114

1 Answers1

3

Actually I just found a solution to my problem!

So here is my solution:

my $c = shift; #receive request

my $user   = $c->param('user_name'); #get parameters
my $response_url = $c->param('response_url');
my $text = $c->param('text');

my $response_body = { #create the imidiate response that Slack is waiting for
            response_type => "ephemeral",
            text          => "Running for $user:",
            attachments   => [
                { text => 'analyze' },
            ],
        };

my $subprocess = Mojo::IOLoop::Subprocess->new; #create the subprocesses
$subprocess->run(
sub {do_time_consuming_things($user,$response_url,$text)}, #this callback is the
#actuall subprocess that will run in background and contains the POST request
#from my "fork" code (with the output) that should send a late response to Slack
sub {# this is a dummy subprocess doing nothing as this is needed by Mojo.
    my ($subprocess, $err, @results) = @_;
    say $err if $err;
    say "\n\nok\n\n";
}
);
#and here is the actual imidiate response outside of the subprocesses in order
#to avoid making the server wait for the subprocess to finish before responding!
$c->render( json => $response_body ); 

So I actually simply had to put my code of do_time_consuming_things in the first callback (in order for it to run as a subprocess) and use the second callback (that is actually linked to the parent process) as a dummy one and keep my "imidiate" response in the main body of the whole function instead of putting it inside one of the subprocesses. See code comments for more information!

  • I didn't get the reason for a downvote in my answer :( . Please at least leave a comment when you downvote so people with low expirience on stackoverflow (like me) can have an actual feedback of what they did wrong...) – Spyratos Aggelos Sep 13 '18 at 12:43
  • Not sure why you got the downvote - maybe is because you mixed your reply to my comment into your answer. Would suggest to remove that bit from the answer and make it an actual comment next to mine. that way other people will find it easier to understand and read the whole thing. – Erik Kalkoken Sep 13 '18 at 13:34
  • FYI you should use Mojo::IOLoop->subprocess to spawn subprocesses rather than creating the object yourself; and since you're in the context of a Mojolicious controller action, [Mojolicious::Plugin::Subprocess](https://metacpan.org/pod/Mojolicious::Plugin::Subprocess) will handle it even better. – Grinnz Sep 18 '18 at 23:09