lambda_punch icon indicating copy to clipboard operation
lambda_punch copied to clipboard

Invoking an existing lambda will trigger an error on 'start_server!'

Open brcarp opened this issue 2 years ago • 5 comments

The suggested configuration here:

https://github.com/rails-lambda/lambda_punch/blob/77e05e302bc59fd5588383a4188a53e2f875eb18/README.md?plain=1#L42

... will cause an Errno::EADDRINUSE error to be raised if a lambda is invoked manually, because the LambdaPunch server will have already been started. A call to aws lambda invoke for example yields the following error and stack trace:

Address already in use - bind(2) for "127.0.0.1" port 9030
	/usr/local/lib/ruby/2.7.0/drb/drb.rb:883:in 'initialize':
	    Address already in use - bind(2) for "127.0.0.1" port 9030 (Errno::EADDRINUSE)
	from /usr/local/lib/ruby/2.7.0/drb/drb.rb:883:in 'open'
	from /usr/local/lib/ruby/2.7.0/drb/drb.rb:883:in 'open_server'
	from /usr/local/lib/ruby/2.7.0/drb/drb.rb:766:in 'block in open_server'
	from /usr/local/lib/ruby/2.7.0/drb/drb.rb:764:in 'each'
	from /usr/local/lib/ruby/2.7.0/drb/drb.rb:764:in 'open_server'
	from /usr/local/lib/ruby/2.7.0/drb/drb.rb:1466:in 'initialize'
	from /usr/local/lib/ruby/2.7.0/drb/drb.rb:1772:in 'new'
	from /usr/local/lib/ruby/2.7.0/drb/drb.rb:1772:in 'start_service'
	from /app/vendor/bundle/ruby/2.7.0/gems/lambda_punch-1.0.3/lib/lambda_punch/server.rb:22:in 'initialize'
	from /usr/local/lib/ruby/2.7.0/singleton.rb:125:in 'new'
	from /usr/local/lib/ruby/2.7.0/singleton.rb:125:in 'block in instance'
	from /usr/local/lib/ruby/2.7.0/singleton.rb:123:in 'synchronize'
	from /usr/local/lib/ruby/2.7.0/singleton.rb:123:in 'instance'
	from /app/vendor/bundle/ruby/2.7.0/gems/lambda_punch-1.0.3/lib/lambda_punch/server.rb:15:in 'start!'
	from /app/vendor/bundle/ruby/2.7.0/gems/lambda_punch-1.0.3/lib/lambda_punch.rb:36:in 'start_server!'
	from /app/config/environments/production.rb:141:in 'block (2 levels) in <main>'

I was able to work around this doing something like the following:

  config.to_prepare do
    LambdaPunch.start_server!
  rescue Errno::EADDRINUSE => e
    Rails.logger.info("LambdaPunch is already running! (#{e.class})")
  end

Is there a better way to do this, or is there something that LambdaPunch can support natively to check to see if a server is already running, e.g. an alive? predicate similar to what's in DRb?

brcarp avatar Jun 09 '23 14:06 brcarp

💭 Another idea i had was to investigate ARGV and maybe skip the start_server! call if Rails was executed with runner?

Unfortunately RunnerCommand does ARGV.replace(command_argv) which basically removes runner from ARGV. 😞

brcarp avatar Jun 09 '23 16:06 brcarp

💭 Another idea i had was to investigate ARGV and maybe skip the start_server! call if Rails was executed with runner?

Unfortunately RunnerCommand does ARGV.replace(command_argv) which basically removes runner from ARGV. 😞

Ooh, what if I add if Rails.const_defined?(:Server)? That might be sufficient to distinguish rails server from rails runner.

brcarp avatar Jun 09 '23 16:06 brcarp

Oh, I see the problem. Invoking is not the issue, technically there is no double init process for Lambda. What is happening is that when you are invoking, you are starting another Rails process off on the same function. Never anticipated that. I think ideally you could talk to the existing process. Not only would the that mean the "runner" action(s) and even "tasks" would be faster... but it would mean you can rely on LambdaPunch just working inside those tasks without coordination. How do you feel about the following.

We make a small change to Lamby's cmd handler (https://github.com/rails-lambda/lamby/blob/master/lib/lamby/handler.rb#L84-L103) to have one more... the last thing we will ever need... a basic method executor interface.

{
  "lamby": {
    "cmd": {
      "method": "SomeConstant.method",
      "args": ["..."]
    }
  }
}

So the idea here would be that anything would work here. We can use this vs the runner. Builders can add syntactic sugar to this. For example, want to call a migration? There are top level methods to do that. We could even build a virtual REPL on this if the interface detected an array of commands. Thoughts? See where I am headed?

metaskills avatar Jun 12 '23 11:06 metaskills

Ooh, what if I add if Rails.const_defined?(:Server)? That might be sufficient to distinguish rails server from rails runner.

For the record, this does NOT work. These Rails lambdas are not running "rails server", which I might have realized if I thought about it for two seconds.

brcarp avatar Jun 12 '23 17:06 brcarp

@brcarp Would you like to make a PR to LambdaPunch that does the Errno::EADDRINUSE in the gem? That sounds like the best way forward?

metaskills avatar Jun 14 '23 11:06 metaskills

Added this in v1.1.4.

metaskills avatar Jun 09 '24 23:06 metaskills