Skip to content

Instantly share code, notes, and snippets.

@yaauie
Created November 17, 2013 02:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yaauie/7508274 to your computer and use it in GitHub Desktop.
Save yaauie/7508274 to your computer and use it in GitHub Desktop.

My general thoughts on Resque Next is that it should be more of a protocol and an interface than an end-all-be-all solution for delayed work in Ruby, that it should provide at least one implementation for each of the interfaces, but that it should leave further implementation up to third-parties and separate include-if-you-want-it gems (that may be published to either the resque org or elsewhere).

This includes the dependency on Redis. When a consumer out-grows Redis and needs to use a queueing service that has actual, non-emulated queueing semantics, they shouldn't have to rewrite all of their worker code and start over from scratch; when they become entirely IO-bound and need thread-based workers, they should be able to swap out the worker class and everything should just work.

And I don't know if we can get there in a step-by-step, one-thing-at-a-time refactor. Maybe.

Here are some pseudo-code thoughts on interfaces and boundaries:

module Resque
  # passthrough to Redis::Backend
  def self.new(*args, &block)
    Backend.new(*args, &block)
  end

  module QueueProvider
    # provides interface for *all* queueing semantics
    # (queue, queue_at, reserve, ack, fail, heartbeat, etc.)
    # and queue introspection/manipulation.
    # (list, count, move, drop)
  end

  class QueueProvider::Redis
    include QueueProvider
    # provides implementation of QueueProvider
    # with emulated queueing semantics via redis
  end

  module Coder
    # provide interface for *all* encode/decode
  end

  class Coder::JSON
    include Coder
    # provides implementation of Coder via JSON
  end

  class Backend
    # In my mind, the Backend is the core of everything, the instance that
    # gets passed around to the workers that use it, the queue providers,
    # the everything. It holds a config hash or object and makes decisions
    # based on that configuration.
    def intialize(config = {})
      @config = config.dup
    end

    def queue_provider
      @queue_provider ||= begin
        provider_class = @config.fetch(:queue_provider) { QueueProvider::Redis }
        provider_class.new(self)
      end
    end
    # and then use Forwardable to delegate queueing semantics to
    # the queue provider.

    def coder
      @coder ||= begin
        coder_class = @config.fetch(:coder) { Coder::JSON }
        coder_class.new(self)
      end
    end

    def worker(queues)
      @worker_class ||= @config.fetch(:worker) { Worker::Forked }
      @worker_class.new(self, queues)
    end
  end

  module Worker
    # provides interface for a worker.
  end

  class Worker::Forked
    include Worker
    # provides implementation of Worker, near-identical to the current
    # 1-x implementation.
  end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment