0.0
No release in over 3 years
There's a lot of open issues
...
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

 Project Readme

CloudContext

Propagate request-scoped context across the distributed systems in your stack.

CloudContext is a thread-local key/value store that rides along with downstream HTTP requests (via headers) and Sidekiq jobs (via job metadata), so request-scoped data — request ids, tenant ids, user ids, feature flags, locale, etc. — flows automatically across service and process boundaries without having to thread it through every method signature.

require 'cloud_context'

CloudContext['request_id'] = 'abc-123'
CloudContext['tenant_id']  = 42

# ...any HTTP call made with Faraday or Sidekiq job enqueued
#    downstream will carry these values along.

Installation

# Gemfile
gem 'cloud_context'
bundle install

Usage

CloudContext behaves like a Hash, scoped to the current thread:

CloudContext['request_id'] = 'abc-123'
CloudContext['request_id']           # => "abc-123"
CloudContext.fetch('request_id')     # => "abc-123"
CloudContext.fetch('missing')        # => raises KeyError
CloudContext.to_h                    # => { "request_id" => "abc-123" }

CloudContext.update(user_id: 7, tenant_id: 42)

CloudContext.delete('request_id')
CloudContext['tenant_id'] = nil      # equivalent to .delete
CloudContext.clear

CloudContext.empty?                  # => true
CloudContext.size                    # => 0

Keys are stringified, so :user_id and 'user_id' are equivalent.

Values must be round-trippable through JSON — strings, numbers, booleans, arrays, and hashes are fine. Symbols, Time, and other non-JSON-native types will raise ArgumentError at assignment time so problems surface at the call site rather than across the wire.

CloudContext['user_id'] = :alice      # raises ArgumentError
CloudContext['ts']      = Time.now    # raises ArgumentError

Propagating across the network

Rails

Just require it. The Railtie installs Rack middleware that hydrates CloudContext from the incoming request and isolates each request's context from the next.

# Gemfile
gem 'cloud_context'

Rack

For non-Rails Rack apps, mount the middleware:

require 'cloud_context/rack'

use CloudContext::Rack::Adapter

Faraday

Add the middleware to any Faraday connection that should forward context downstream:

require 'cloud_context/faraday'

conn = Faraday.new(url: 'https://service.internal') do |f|
  f.use :cloud_context
  f.adapter Faraday.default_adapter
end

CloudContext['request_id'] = 'abc-123'
conn.get('/widgets')   # sends X-Cloud-Context: {"request_id":"abc-123"}

Per-connection overrides:

# send a different header name
f.use :cloud_context, header: 'X-Trace-Context'

# send a custom payload instead of CloudContext.to_h
f.use :cloud_context, context: -> { { request_id: Current.request_id } }

Sidekiq

Requiring cloud_context/sidekiq installs both client and server middleware automatically, so context propagates from the enqueuer into the worker — and from one worker into any jobs it enqueues.

require 'cloud_context/sidekiq'

CloudContext['request_id'] = 'abc-123'
SomeWorker.perform_async   # job carries CloudContext along

Scoping context with contextualize

contextualize runs a block with a fresh, empty context, then restores the previous one — even if the block raises. This is what the Rack and Sidekiq adapters use internally to isolate one request or job from the next, and it's also useful directly:

CloudContext['tenant_id'] = 42

CloudContext.contextualize do
  CloudContext['tenant_id'] = 99
  do_some_work   # sees tenant_id => 99
end

CloudContext['tenant_id']   # => 42

Configuration

By default, CloudContext uses the HTTP header X-Cloud-Context. To change it globally:

CloudContext.http_header = 'X-Trace-Context'

Header names are normalized to uppercase with underscores, matching Rack's HTTP_* env conventions.

Testing with RSpec

To clear CloudContext between examples, enable the RSpec adapter in your spec_helper.rb:

require 'cloud_context/rspec'

CloudContext::RSpec.enable

This installs an after(:example) hook that calls CloudContext.clear. Call CloudContext::RSpec.disable to turn it off without removing the hook.

Size Limitations

While there is technically no limit to header size, servers generally enforce limits. For example, Apache's default is 8KB and will return HTTP 413 - Entity Too Large. Headers also add overhead to every request, so use CloudContext sparingly. To approximate byte size:

CloudContext.bytesize

Threading

CloudContext allocates a separate context per thread (and per Fiber). Contexts are not shared across threads, so worker pools and async frameworks won't leak state between concurrent units of work.


Contributing

Yes please :)

  1. Fork it
  2. Create your feature branch (git checkout -b my-feature)
  3. Ensure the tests pass (bundle exec rspec)
  4. Commit your changes (git commit -am 'awesome new feature')
  5. Push your branch (git push origin my-feature)
  6. Create a Pull Request

Inspired by


Gem codecov