Using ConsoleKit¶
Usage¶
After installing, generate the initializer and configuration files by running:
rails generate console_kit:install
Then, edit config/initializers/console_kit.rb to define your tenants and context class. Example format:
ConsoleKit.configure do |config|
config.tenants = {
tenant_one: {
constants: {
shard: :tenant_one_db,
mongo_db: :tenant_one_mongo,
partner_code: 'partnerA',
redis_db: 1,
elasticsearch_prefix: 'tenant_one',
environment: 'production'
}
},
tenant_two: {
constants: {
shard: :tenant_two_db,
mongo_db: :tenant_two_mongo,
partner_code: 'partnerB',
redis_db: 2,
elasticsearch_prefix: 'tenant_two',
environment: 'staging'
}
}
}
config.context_class = CurrentContext
# Optional: Toggle pretty CLI output
config.pretty_output = true
# Optional: Show connection dashboard on tenant switch (default: false)
# config.show_dashboard = true
end
A constants key you omit is not left alone: switching to that tenant resets that backend to its
default. That is deliberate - leaving Redis on the previous tenant while SQL moves would be exactly
the mixed-tenant state this gem exists to prevent. Only shard and partner_code are required.
Supported Connections¶
ConsoleKit automatically detects and manages connections for:
| Connection | Gem Required | Config Key | How it switches | How it is verified |
|---|---|---|---|---|
| SQL (ActiveRecord) | activerecord |
shard |
connecting_to(shard:) for registered shards, establish_connection otherwise |
current_shard / db_config.name |
| MongoDB | mongoid |
mongo_db |
Mongoid.override_client for named clients, override_database otherwise |
effective client or database name |
| Redis | redis |
redis_db |
SELECT on the application's Redis handle |
the client's cached logical DB |
| Elasticsearch | elasticsearch |
elasticsearch_prefix |
Elasticsearch::Model.index_name_prefix= |
the effective index prefix, read back |
Handlers are only activated when their corresponding gem is loaded. Verification is a local read on every backend - switching a tenant makes no diagnostic network calls.
Programmatic API¶
The console prompt is the usual entry point, but the same operations are available directly:
# Raising, programmatic switch. Atomic: on failure the previous tenant is restored.
ConsoleKit.switch_tenant(:acme)
# The current tenant key, or nil.
ConsoleKit.current_tenant # => :acme
# Re-verify that every available backend still points at the current tenant.
ConsoleKit.verify_tenant!
# Nested, exception-safe scope. The enclosing tenant is restored on exit,
# including when the block raises or the inner switch fails.
ConsoleKit.with_tenant(:globex) do
Order.count
end
# back on :acme here
# Validate the whole configuration up front. Reports every problem at once.
ConsoleKit.configuration.validate!
ConsoleKit.switch_tenant raises on failure. The interactive console flow deliberately does not -
it reports through the console output and returns false, so a mistyped tenant does not tear down
your session.
Not every failure is a TenantSwitchError. Problems found before anything is applied - an unknown
tenant, malformed constants, a backend that cannot support the request - raise
TenantNotFoundError, ConfigurationError or UnsupportedBackendError directly, because there is
nothing to roll back. Rescue ConsoleKit::Error if you want to catch all of them:
begin
ConsoleKit.switch_tenant(:globex)
rescue ConsoleKit::TenantSwitchError => e
# the switch was attempted and rolled back
warn e.message unless e.rollback_succeeded?
rescue ConsoleKit::Error => e
# rejected before anything was touched
warn e.message
end
verify_tenant! raises ConnectionVerificationError on a mismatch and does not roll back - it
is a report on the current state, not a repair. If it fails, the backends really are inconsistent
and you should switch again explicitly. It checks against the tenant constants frozen at the moment
you switched, so reloading or replacing your configuration afterwards does not make it lie.
It also reports backends that were never switched at all. A handler that exists but is broken is dropped from the switch, and a switch that silently skipped a backend must not be allowed to look fully verified:
ConsoleKit.verify_tenant!.dropped_backends
# => [:elasticsearch] # this backend was never switched, verified or rolled back
A backend whose gem simply is not installed is not "dropped" - that is a supported setup and is never reported.
Console Usage¶
When launching the Rails console, ConsoleKit will prompt you to select a tenant (if multiple tenants are configured). On selection, a tenant banner is displayed showing the tenant name, environment safety warnings, and active connections.
Selection Options:¶
- Number or Name: Select a tenant by its index or name (case-insensitive).
- 0 (Skip): Load the console without any tenant configuration.
- exit / quit: Immediately terminate the console session.
Console Helpers¶
The following helper methods are available in your Rails console:
# Switch to a different tenant
switch_tenant
# Print details about the current tenant
tenant_info
# List all available tenants
tenants
# Show connection diagnostics dashboard
dashboard
Custom Prompt¶
ConsoleKit automatically sets your IRB/Pry prompt to show the active tenant:
[tenant_one] main:001>
Other Methods¶
# Get current tenant
ConsoleKit.current_tenant
# => :tenant_one
# Reset and re-select tenant
ConsoleKit.reset_current_tenant
# Toggle pretty output
ConsoleKit.enable_pretty_output
ConsoleKit.disable_pretty_output
See Programmatic API for switch_tenant, with_tenant and verify_tenant!.
Connection Dashboard¶
Run dashboard in the console to see a diagnostics table for all active connections:
--- Connection Dashboard ---
┌───────────────┬─────────────┬─────────┬──────────────────────────────────────────┐
│ Service │ Status │ Latency │ Details │
├───────────────┼─────────────┼─────────┼──────────────────────────────────────────┤
│ SQL │ ✓ Connected │ 1.1ms │ adapter: PostgreSQL, pool_size: 5, ... │
│ MongoDB │ ✓ Connected │ 2.3ms │ database: my_tenant_db, version: 8.0.20 │
│ Redis │ — N/A │ — │ │
│ Elasticsearch │ ✗ Error │ — │ error: connection refused │
└───────────────┴─────────────┴─────────┴──────────────────────────────────────────┘
The dashboard takes a level:
dashboard # :basic - no network calls at all
dashboard(level: :full) # adds version, health and latency probes
:basic is the default and reports only what can be read locally, so it is cheap enough to run
freely. Switching a tenant never triggers diagnostics on its own.
Both levels run in your own thread, so they report the connections your console is actually using. That matters more than it sounds: a backend's tenant lives in thread-local state, so a check run on a worker thread would inspect a different tenant than the one you are on.
The cost is that :full has no timeout of its own - a backend that hangs holds the dashboard until
its own client gives up, and Ctrl-C interrupts it. The timeout: option reports a budget rather
than enforcing one; an overrun is counted, not cut off. Bound a slow backend by configuring a
timeout on that client, which is the only layer that can cancel its own call safely. :basic never
leaves the process, so none of this applies to it.
Only :full results are cached, for a couple of seconds; :basic is never cached, because it is a
local read and caching it bought nothing while costing correctness.
A cached :full row is reused only while all three hold: the TTL has not elapsed, the calling thread
is still on the same tenant state, and the backend still reports the same observed identity - the
Elasticsearch prefix, the Redis logical DB, the SQL pool. So another thread moving a process-global
backend drops the row on the next render rather than serving it stale. That identity check is a
memory read, so a cached render still costs zero round trips. What can be up to a cache window old is
only the measured part of a row - latency, cluster health, version, memory - never which tenant a
backend is on.
To auto-display the dashboard on every tenant switch, add to your initializer:
config.show_dashboard = true
Environment Warnings¶
When a tenant has an environment key in its constants:
- production: A red warning is displayed at setup time.
- staging: A yellow warning is displayed at setup time.