Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

while_executing with on_conflict: :reschedule reschedules job when unlock fails #770

Closed
dsander opened this issue Apr 13, 2023 · 7 comments · Fixed by #771
Closed

while_executing with on_conflict: :reschedule reschedules job when unlock fails #770

dsander opened this issue Apr 13, 2023 · 7 comments · Fixed by #771

Comments

@dsander
Copy link
Contributor

dsander commented Apr 13, 2023

Hi there! 👋

I am not 100% sure if this really is a bug or if I am not understanding the use case.

Describe the bug

def execute(&block)
with_logging_context do
executed = locksmith.execute do
yield
callback_safely if locksmith.unlock
ensure
locksmith.unlock
end
unless executed
reflect(:execution_failed, item)
call_strategy(origin: :server, &block)

Because executed captures the return value of the locksmith.execute block it will be falsy when the unlock operation failed (and when callback_safely returns something fasly?).

We noticed the behaviour on v7 with a sidekiq version that changed the redis key that hold the busy workers (#756), in our case this lead to a job being run over and over because unlock failed (the reaper deleted the lock of the running job).

Expected behavior

The server server strategy should only be called when the job lock could not be acquired, not when the unlock failed (successfully 😉 )

Maybe something like this?

      def execute(&block)
        with_logging_context do
          executed = false
          locksmith.execute do
            yield
            executed = true
            callback_safely if locksmith.unlock
          ensure
            locksmith.unlock
          end

          unless executed
            reflect(:execution_failed, item)
            call_strategy(origin: :server, &block)
          end
        end
      end

Worker class

class MyWorker
  include Sidekiq::Worker

  sidekiq_options queue: :assets, retry: false, lock: :while_executing, on_conflict: :reschedule
  
  ....
end
@mhenrixon
Copy link
Owner

Is that only for blocks? For a rescue ensure scenario in a method, the ensure part would only return something with an explicit return. Are blocks different?

@mhenrixon
Copy link
Owner

Try this in the console:

CleanShot 2023-04-13 at 13 08 03@2x

****

@mhenrixon
Copy link
Owner

If the ensure part would indeed return something it would say "pokus" not "hokus".

@dsander
Copy link
Contributor Author

dsander commented Apr 13, 2023

In our case I believe this happened:

irb(main):015:0* block = lambda do
irb(main):016:0*   true
irb(main):017:0*   "i don't know the return value" if false
irb(main):018:0* ensure
irb(main):019:0*   false
irb(main):020:-> end
irb(main):021:-> pp block.call
nil
=> nil

If unlock succeeds everything works as you mentioned:

irb(main):022:0* block = lambda do
irb(main):023:0*   true
irb(main):024:0*   "i don't know the return value" if true
irb(main):025:0* ensure
irb(main):026:0*   false
irb(main):027:-> end
"i don't know the return value"
=> "i don't know the return value"

@dsander
Copy link
Contributor Author

dsander commented Apr 13, 2023

So maybe a different approach to solve this would be

      def execute(&block)
        with_logging_context do
          executed = locksmith.execute do
            yield
          ensure
            callback_safely if locksmith.unlock
          end

          unless executed
            reflect(:execution_failed, item)
            call_strategy(origin: :server, &block)
          end
        end
      end

? To be honest I wasn't aware that the result of the ensure block isn't the return value of the block 😉

@mhenrixon
Copy link
Owner

Ah now I see your problem doh 🙄

@dsander
Copy link
Contributor Author

dsander commented Apr 13, 2023

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants