Git Product home page Git Product logo

suo's People

Contributors

avokhmin avatar doits avatar gandalfthegui avatar jeremywadsack avatar levkk avatar mlarraz avatar nickelser avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

suo's Issues

Require either redis or dalli but not both

Right now, your gemspec has the following:

  spec.add_dependency "dalli"
  spec.add_dependency "redis"

We only use Redis as a backend, not Memcached. Adding the dependency on dalli bloats our code.

Would it be possible to make this a dynamic dependency? It seems that the Gemfile using Suo is likely to already have redis or dalli included, so can you just skip the dependencies and load them on-demand? (You'd still need them in development_dependencies for tests.) I think you'd need a note in the README about that requirement.

Unsafe distributed lock with Redis

Hey guys,

I stumbled upon this gem while working on an updated test suite for activejob-locking. When I was using the Redis client/implementation my tests for parallel lock acquisition failed, so the lock was over-acquired. (Eg. 3 time parallel processes, on the same machine or on different machines, could acquire the same lock, each)

See: https://bit.ly/3LAT9cl

The issue is based on the Redis watch command in case there is no such key in the first place. To work around this a user must make sure to set the key (to an empty string) before any process wants to acquire the lock. This can be done at application boot or while deployments.

I set up a demo repository for reproducibility: https://github.com/Jack12816/suo-dlm-redis-bug

The Memcached client/implementation does not suffer from this issue as far as I can tell.


See Redlock for a canonical Redis DLM algorithm as an alternative, in case you care about the safety guarantee that at any given moment, only one client can hold a lock.


Proposal: Maybe we could add a hint about this in the readme?

Am I able to lock it by instance?

Let's say I have an "Organization" model that the instances perform a few different rake tasks when called or triggered with whenever.
Am I able to lock the code block accordingly to the organization_id, allowing Org(id:2) to call the task even if Org(id:1) is already executing it, cause it's Org(id:2)'s first call?

Since I have a few types of rake tasks, can I lock it accordingly to the task type as well? Allowing Org(id:1) call task_1 and task_2 simultaneously?

Potential for lock/unlock to never happen with short `acquisition_timeout`

This is similar to #4 (and related to why I discovered that in the first place).

We set acquisition_delay to 0.001 and acquisition_timeout to 0.002 because the default delay of 0.01 was causing job enqueuing to take a very long time when enqueuing thousands of jobs.

In Suo::Client::Base#retry_with_timeout there are some things I'm trying to wrap my head around with this design, so maybe you can shed some light:

      def retry_with_timeout
        start = Time.now.to_f

        retry_count.times do
          elapsed = Time.now.to_f - start
          break if elapsed >= options[:acquisition_timeout]

          synchronize do
            yield
          end

          sleep(rand(options[:acquisition_delay] * 1000).to_f / 1000)
        end
      rescue => _
        raise LockClientError
      end

Potential to never run the block
If acquisition_timeout is really low (as in our case) then the block never gets run. Shouldn't this ensure that the block gets run at least once?

Passed in block must break out
If we look at the default values:

      DEFAULT_OPTIONS = {
        acquisition_timeout: 0.1,
        acquisition_delay: 0.01,
        ...

Then wouldn't this potentially attempt to get the lock 10 times (0.01s delay for each pass until 0.1s have passed)? I see that in every use, you use break or return to exit the block that's yielded in #retry_with_timeout so that the delay isn't hit if the action succeeds. So this doesn't seem to be an issue in how it's used but could be a problem in a later use.

I'm happy to work on a PR for an update to this if either of these concerns is correct.

Lock acquisition is delayed for two tries on initial set of lock

If a lock key has never been set, Suo::Client::Base#acquire_lock retries the acquisition rather than returning the token:

      retry_with_timeout do
          val, cas = get

          if val.nil?
            initial_set
            next
          end

        ...
      end

The next in the code above is effectively equivalent to not having that line (because the next retry would pass to the code following which clears locks). Is it necessary to make that pass? It seems that it should return token here.

Because of this, Suo won't acquire a lock unless the retry count is 2 or more. So if I configure with :acquisition_timeout less than or equal to : acquisition_delay, even though @retry_count gets the ceil of the dividend, 1 try isn't enough to acquire a lock.

I'm happy to open a PR to change this, but I want to make sure I understand this correctly and I'm not missing something important.

gemspec line #19

Please remove line #19 from your gemspec file. Its boilerplate. You have no real global executables in you bin directory.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.