Component Host

The component host is the machinery of a service. It's the part that is closest to the operating system. From a technical and physical perspective, the component host is the service.

The word "service" has both a logical architectural meaning and a physical architectural meaning. Physically, a service is just a process running on a computer. Components are what is run inside a service.

Components are hosted inside a service. The ComponentHost is the machinery that does the hosting, and coordinates the interaction with the operating system, including loading and starting of components, signal trapping from the operating system, and the safe and graceful shutdown of a service's components.

The component host starts a component's consumers, and runs them indefinitely until the operating system process is directed to shut down.

Example

# The "component initiator" binds consumers to their streams and starts
# the consumers
# Until this point, handlers have no knowledge of which streams they process
# Starting the consumers starts the stream readers and gets messages flowing
# into the consumer's handlers
module SomeComponent
  def self.call
    command_stream_name = 'something:command'
    SomeConsumer.start(command_stream_name)
  end
end

# ComponentHost is the runnable part of the service
# Register a component module with the component host, then start the host
# and messages sent to its streams are dispatched to the handlers
component_name = 'some-component'
ComponentHost.start(component_name) do |host|
  host.register(SomeComponent)
end

Facts

  • Component host can run any number of components
  • Each component runs in its own isolated actor
  • Each component uses its own message store session
  • When the host shuts down, each subordinate consumer is allowed to finish its current work before the process terminates

Starting the Component Host

self.start(name, &block)

Parameters

NameDescriptionType
nameName of the process (used for logging)String
blockBlock used for registering components that will be run by the hostProc

Registering Components

component_name = 'some-component'
ComponentHost.start(component_name) do |host|
  host.register(SomeComponent, "A Component")
end

The host parameter passed to the registration block defines the register method that receives a component initiator as an argument.

host.register(component_initiator, name=nil)

Parameters

NameDescriptionType
component_initiatorA callable that starts consumersCallable
NameOptional name of the component initiator (used for logging)String

Component Initiator

A component initiator is any callable that starts consumers. As long as the object responds to the call method (and consequently can be invoked with the () operator), it can be used as a component initiator.

The following examples are all equivalent.

Using a class or module

module SomeClassInitator
  def self.call
    SomeConsumer.start('someStream')
  end
end

Using an object

class SomeInstanceInitiator
  def call
    SomeConsumer.start('someStream')
  end
end

initiator = SomeInstanceInitiator.new

Using a Proc

initiator = proc { SomeConsumer.start('someStream') }

Error Handling

Unhandled Errors

If an error is not handled before it is raised to the level of the component host, it cannot be prevented from causing the process to terminate.

To prevent an error from being raised to the level of the component host, errors must be handled at the consumer level.

See the error handling section of the consumer user guide for details: http://docs.eventide-project.org/user-guide/consumers.html#error-handling.

Recording or Reporting Errors

While errors cannot be handled by the component host, they can optionally be reported or recorded.

ComponentHost.start(component_name) do |host|
  host.record_error(error) do
    SomeErrorReporter.(error)
  end
end
record_error(error)

Parameters

NameDescriptionType
errorUnhandled error that is raised to the level of the component hostRuntimeError

Stopping the Component Host

Graceful Shutdown

The process host affords graceful and safe shutdown for all of its subordinate consumers. When the host is shut down, its subordinate consumers will finish any work-in-progress.

WARNING

If the process is terminated using the operating system's KILL signal, consumers and handlers will not shutdown gracefully. It's not safe to kill the process. Use the KILL signal only when it's absolutely unavoidable.

Using the Keyboard

With the host process is running in a foreground terminal window:

  • CTRL+C: Shut down the process
  • CTRL+Z: Pause the process

Signals

The component host can be stopped safely by sending either the INT or TERM signal to the process.

kill -s SIGINT {process_id}
kill -s SIGTERM {process_id}

Signals

For more background on operating systems signals, see: https://en.wikipedia.org/wiki/Signal_(IPC)

The process host responds to the following operating system signals.

SIGINT and SIGTERM

Safely shuts down the process.

SIGTSTP

Pauses the process.

SIGCONT

Resumes a process paused with the TSTP signal.

Log Tags

The following tags are applied to log messages recorded by the component host:

TagDescription
component_hostApplied to all log messages recorded inside the ComponentHost namespace

The following tags may be applied to log messages recorded by the component host:

TagDescription
componentApplied to messages that pertain to a component
actorApplied to messages recorded by actors
lifecycleApplied to messages that pertain to the lifecycle of a actors
crashApplied to messages recorded while the host is terminating due to an error
signalApplied to messages recorded when handling operating system signals
startApplied to messages recorded when starting an actor
stopApplied to messages recorded when stopping an actor

See the logging user guide for more on log tags.