Module: Semian
- Extended by:
- Semian, Instrumentable
- Included in:
- Semian
- Defined in:
- lib/semian.rb,
lib/semian/adapter.rb,
lib/semian/version.rb,
lib/semian/platform.rb,
lib/semian/resource.rb,
lib/semian/simple_state.rb,
lib/semian/instrumentable.rb,
lib/semian/simple_integer.rb,
lib/semian/circuit_breaker.rb,
lib/semian/protected_resource.rb,
lib/semian/unprotected_resource.rb,
lib/semian/simple_sliding_window.rb,
lib/semian/net_http.rb,
lib/semian/mysql2.rb,
lib/semian/redis.rb
Overview
Overview
Semian is a library that can be used to control access to external services.
It’s desirable to control access to external services so that in the case that one is slow or not responding, the performance of an entire system is not compromised.
Semian uses the concept of a “resource” as an identifier that controls access to some external service. So for example, “mysql” or “redis” would be considered resources. If a system is sharded, like a database, you would typically create a resource for every shard.
Resources are visible across an IPC namespace. This means that you can register a resource in one process and access it from another. This is useful in application servers like Unicorn that are multi-process. A resource is persistent. It will continue to exist even after the application exits, and will only be destroyed by manually removing it with the ipcrm
command, calling Resource.destroy, or rebooting the machine.
Each resource has a configurable number of tickets. Tickets are what controls access to the external service. If a client does not have a ticket, it cannot access a service. If there are no tickets available, the client will block for a configurable amount of time until a ticket is available. If there are no tickets available after the timeout period has elapsed, the client will be unable to access the service and an error will be raised.
Resources also integrate a circuit breaker in order to fail faster and to let the resource the time to recover. If ‘error_threshold` errors happen in the span of `error_timeout` then the circuit will be opened and every attempt to acquire the resource will immediately fail.
Once in open state, after ‘error_timeout` is elapsed, the ciruit will transition in the half-open state. In that state a single error will fully re-open the circuit, and the circuit will transition back to the closed state only after the resource is acquired `success_threshold` consecutive times.
A resource is registered by using the Semian.register method.
Examples
Registering a resource
Semian.register(:mysql_shard0, tickets: 10, timeout: 0.5, error_threshold: 3, error_timeout: 10, success_threshold: 2)
This registers a new resource called :mysql_shard0
that has 10 tickets and a default timeout of 500 milliseconds.
After 3 failures in the span of 10 seconds the circuit will be open. After an additional 10 seconds it will transition to half-open. And finally after 2 successulf acquisitions of the resource it will transition back to the closed state.
Using a resource
Semian[:mysql_shard0].acquire do
# Perform a MySQL query here
end
This acquires a ticket for the :mysql_shard0
resource. If we use the example above, the ticket count would be lowered to 9 when block is executed, then raised to 10 when the block completes.
Overriding the default timeout
Semian[:mysql_shard0].acquire(timeout: 1) do
# Perform a MySQL query here
end
This is the same as the previous example, but overrides the timeout from the default value of 500 milliseconds to 1 second.
Defined Under Namespace
Modules: Adapter, AdapterError, Instrumentable, Mysql2, NetHTTP, Redis, Simple Classes: CircuitBreaker, InternalError, ProtectedResource, Resource, SyscallError, TimeoutError, UnprotectedResource
Constant Summary collapse
- BaseError =
Class.new(StandardError)
- OpenCircuitError =
Class.new(BaseError)
- MAX_TICKETS =
Maximum number of tickets available on this system.
INT2FIX(system_max_semaphore_count)
- VERSION =
'0.6.1'
Instance Attribute Summary collapse
-
#logger ⇒ Object
Returns the value of attribute logger.
Instance Method Summary collapse
-
#[](name) ⇒ Object
Retrieves a resource by name.
- #destroy(name) ⇒ Object
- #disabled? ⇒ Boolean
- #issue_disabled_semaphores_warning ⇒ Object
-
#register(name, tickets:, permissions: 0660, timeout: 0, error_threshold:, error_timeout:, success_threshold:, exceptions: []) ⇒ Object
Registers a resource.
-
#resources ⇒ Object
Retrieves a hash of all registered resources.
- #retrieve_or_register(name, **args) ⇒ Object
- #semaphores_enabled? ⇒ Boolean
-
#sysv_semaphores_supported? ⇒ Boolean
Determines if Semian supported on the current platform.
Methods included from Instrumentable
notify, subscribe, unsubscribe
Instance Attribute Details
#logger ⇒ Object
Returns the value of attribute logger.
113 114 115 |
# File 'lib/semian.rb', line 113 def logger @logger end |
Instance Method Details
#[](name) ⇒ Object
Retrieves a resource by name.
155 156 157 |
# File 'lib/semian.rb', line 155 def [](name) resources[name] end |
#destroy(name) ⇒ Object
159 160 161 162 163 |
# File 'lib/semian.rb', line 159 def destroy(name) if resource = resources.delete(name) resource.destroy end end |
#disabled? ⇒ Boolean
13 14 15 |
# File 'lib/semian/platform.rb', line 13 def disabled? ENV['SEMIAN_SEMAPHORES_DISABLED'] end |
#issue_disabled_semaphores_warning ⇒ Object
91 92 93 94 95 96 97 98 99 |
# File 'lib/semian.rb', line 91 def issue_disabled_semaphores_warning return if defined?(@warning_issued) @warning_issued = true if !sysv_semaphores_supported? logger.info("Semian sysv semaphores are not supported on #{RUBY_PLATFORM} - all operations will no-op") elsif disabled? logger.info("Semian semaphores are disabled, is this what you really want? - all operations will no-op") end end |
#register(name, tickets:, permissions: 0660, timeout: 0, error_threshold:, error_timeout:, success_threshold:, exceptions: []) ⇒ Object
Registers a resource.
name
: Name of the resource - this can be either a string or symbol.
tickets
: Number of tickets. If this value is 0, the ticket count will not be set, but the resource must have been previously registered otherwise an error will be raised.
permissions
: Octal permissions of the resource.
timeout
: Default timeout in seconds.
error_threshold
: The number of errors that will trigger the circuit opening.
error_timeout
: The duration in seconds since the last error after which the error count is reset to 0.
success_threshold
: The number of consecutive success after which an half-open circuit will be fully closed.
exceptions
: An array of exception classes that should be accounted as resource errors.
Returns the registered resource.
137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/semian.rb', line 137 def register(name, tickets:, permissions: 0660, timeout: 0, error_threshold:, error_timeout:, success_threshold:, exceptions: []) circuit_breaker = CircuitBreaker.new( name, success_threshold: success_threshold, error_threshold: error_threshold, error_timeout: error_timeout, exceptions: Array(exceptions) + [::Semian::BaseError], implementation: ::Semian::Simple, ) resource = Resource.new(name, tickets: tickets, permissions: , timeout: timeout) resources[name] = ProtectedResource.new(resource, circuit_breaker) end |
#resources ⇒ Object
Retrieves a hash of all registered resources.
166 167 168 |
# File 'lib/semian.rb', line 166 def resources @resources ||= {} end |
#retrieve_or_register(name, **args) ⇒ Object
150 151 152 |
# File 'lib/semian.rb', line 150 def retrieve_or_register(name, **args) self[name] || register(name, **args) end |
#semaphores_enabled? ⇒ Boolean
9 10 11 |
# File 'lib/semian/platform.rb', line 9 def semaphores_enabled? !disabled? && sysv_semaphores_supported? end |
#sysv_semaphores_supported? ⇒ Boolean
Determines if Semian supported on the current platform.
5 6 7 |
# File 'lib/semian/platform.rb', line 5 def sysv_semaphores_supported? /linux/.match(RUBY_PLATFORM) end |