Project

stockpile

0.0
Repository is archived
No commit activity in last 3 years
No release in over 3 years
Stockpile is a simple key-value store connection manager framework. Stockpile itself does not implement a connection manager, but places expectations for implemented connection managers. So far, only Redis has been implemented (stockpile-redis). Stockpile also provides an adapter so that its functionality can be accessed from within a module. Release 2.0 fixes an issue when Stockpile options are provided with an OpenStruct, originally reported as {stockpile-redis#1}[https://github.com/halostatue/stockpile-redis/issues/1]. Support for Ruby 1.9 has been dropped.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies
 Project Readme

stockpile¶ ↑

code

github.com/halostatue/stockpile/

bugs

github.com/halostatue/stockpile/issues

continuous integration

<img src=“https://travis-ci.org/halostatue/stockpile.svg?branch=master” alt=“Build Status” />

Description¶ ↑

Stockpile is a simple key-value store connection manager framework. Stockpile itself does not implement a connection manager, but places expectations for implemented connection managers. So far, only Redis has been implemented (stockpile-redis).

Stockpile also provides an adapter so that its functionality can be accessed from within a module.

Release 2.0 fixes an issue when Stockpile options are provided with an OpenStruct, originally reported as stockpile-redis#1. Support for Ruby 1.9 has been dropped.

Features¶ ↑

  • Stockpile manages key-value store connections. There are two variants:

    • wide (Stockpile.new, the default), where additional client connections are new instances of the client library;

    • narrow (Stockpile.new(narrow: true)), where additional client connections use the same client library instance.

  • Stockpile can also be injected into a module (Stockpile.inject!(self, options = {})), which gives the module cache management and adapter methods (.cache and .cache_adapter, by default).

Requirements¶ ↑

The desired key-value store must already be installed and/or specified in your Gemfile.

Synopsis¶ ↑

wide = Stockpile.new # A Stockpile instance.
wide.connection.set('hello', 'world') # => 'OK'
wide.connection.get('hello') # => 'world'

# Connections are independent from one another.
wide.connection_for(:other) != wide.connection # => true

# Or set ENV['STOCKPILE_CONNECTION_WDITH'] = 'narrow'
narrow = Stockpile.new(narrow: true) # A 'narrow' Stockpile to Redis.
narrow.connection_for(:other) == narrow.connection # => true

# Special Redis::Namespace handling for Resque. Assumes that redis-namespace
# has been installed, as well.
narrow.connection_for(:resque) != narrow.connection # => true
narrow.connection_for(:resque).redis == narrow.connection # => true

# Standard namespace handling.
narrow.connection_for(:other, namespace: 'other') !=
  narrow.connection # => true
narrow.connection_for(:other, namespace: 'other').redis !=
  narrow.connection # => true

# Show a Stockpile with no adapter capabilities, but name the method
# stockpile, not cache. This will still usefully manage connections.
module Cacher
  Stockpile.inject!(self, method: :stockpile, adaptable: false)
end
Cacher.respond_to?(:stockpile) # => true
Cacher.respond_to?(:stockpile_adapter) # => false
Cacher.stockpile.connection.set('hello', 'world') # => 'OK'
Cacher.stockpile.connection.get('hello') # => 'world'

# Now a Stockpile with adapter capabilities.
module Jobber
  module LastRunTime
    def last_run_time(key, value = nil)
      if value
        connection.hset(__method__, key, value.utc.iso8601)
      else
        value = connection.hget(__method__, key)
        Time.parse(value) if value
      end
    end
  end

  Stockpile.inject!(self)
end
Jobber.respond_to?(:cache) # => true
Jobber.respond_to?(:cache_adapter) # => true

# Four ways:
# 1. Adapt Jobber.cache to recognize #last_run_time.
Jobber.cache_adapter(Jobber::LastRunTime)
Jobber.cache.last_run_time('hello', t = Time.now) # => true
Jobber.cache.last_run_time('hello') # => approximately t

# 2. Adapt Jobber.cache and another module to recognize #last_run_time.
module Foo; end
Jobber.cache_adapter(Jobber::LastRunTime, Foo)
Foo.last_run_time('hello', t = Time.now) # => true
Foo.last_run_time('hello') # => approximately t

# 3. Adapt Jobber.cache and Jobber to recognize #last_run_time.
Jobber.cache_adapter(Jobber::LastRunTime, Jobber)
Jobber.last_run_time('hello', t = Time.now) # => true
Jobber.last_run_time('hello') # => approximately t

# 4. Adapt Jobber.cache and Jobber::LastRunTime to recognize #last_run_time.
Jobber.cache_adapter!(Jobber::LastRunTime)
# or Jobber.cache_adapter(Jobber::LastRunTime, Jobber::LastRunTime)
Jobber::LastRunTime.last_run_time('hello', t = Time.now) # => true
Jobber::LastRunTime.last_run_time('hello') # => approximately t

Background¶ ↑

Stockpile is the evolution of concepts I have applied to Rails applications over the last few years when working with Redis, and avoids the following common but suboptimal patterns:

  • Developers use REDIS or $redis to initialize and access their Redis instances. This could be fixed by using Redis.current, but that still exposes implementation details unnecessarily.

  • Redis methods are often exposed directly in controllers or models, as

    render json: $redis.hget('last_run_time', params[:method])
    

    We don’t like seeing direct database access methods in our controllers, so why do we put up with this for Redis?

  • Each Redis client manages its own connections, and at least one client reconnection is forgotten when using a forking server like Unicorn.

  • Some providers of Redis services restrict the number of simultaneous connections to a given Redis instance. With Rails caching, an application cache, and Resque there are at least three simultaneous connections to Redis for a given Rails server instancne, unless the same connection is reused.

Sample Rails Application¶ ↑

I will be adapting a sample Rails application to demonstrate how Stockpile can be used in Rails. A link to it will be provided here when it is complete.

Install¶ ↑

Stockpile is not intended to be installed by itself, as it does not implement a key-value store specific connection manager. Instead, install a store-specific gem which depends on Stockpile.

gem 'stockpile-redis', '~> 1.1'

Or manually install:

% gem install stockpile-redis

and require Stockpile in your code:

require 'stockpile/redis'

Stockpile Semantic Versioning¶ ↑

Stockpile uses a Semantic Versioning scheme with one change:

  • When PATCH is zero (0), it will be omitted from version references.

:include: Contributing.rdoc

:include: Licence.rdoc