deliveroo/routemaster-drain

View on GitHub
lib/routemaster/dirty/map.rb

Summary

Maintainability
A
35 mins
Test Coverage
require 'delegate'
require 'set'
require 'wisper'

module Routemaster
  module Dirty
    # Collects information about entities whose state has changed and need to be
    # refreshed.
    # Typically +mark+ is called when notified state has changed (e.g. from the
    # bus) and +sweep+ when one wants to know what has changed.
    #
    # Use case: when some entites are very volatile, the map will hold a "dirty"
    # state for multiple updates until the client is ready to update.
    #
    class Map
      include Wisper::Publisher
      KEY = 'dirtymap:items'

      def initialize(redis: nil)
        @redis = redis || Config.drain_redis
      end

      # Marks an entity as dirty.
      # Return true if newly marked, false if re-marking.
      def mark(url)
        @redis.sadd(KEY, url).tap do |marked|
          publish(:dirty_entity, url) if marked
        end
      end

      # Runs the block.
      # The entity will only be marked as clean if the block returns truthy.
      def sweep_one(url, &block)
        return unless block.call(url)
        @redis.srem(KEY, url)
      end

      def all
        @redis.smembers(KEY)
      end

      # Yields URLs for dirty entitities.
      # The entity will only be marked as clean if the block returns truthy.
      # It is possible to call +next+ or +break+ from the block.
      def sweep(limit = 0)
        unswept = []
        while (url = @redis.spop(KEY))
          unswept.push url
          is_swept = !! yield(url)
          unswept.pop if is_swept
          break if (limit -=1).zero?
        end
      ensure
        @redis.sadd(KEY, unswept) if unswept.any?
      end

      # Number of currently dirty entities.
      def count
        @redis.scard(KEY)
      end
    end
  end
end