# 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 (opens new window) is the machinery that does the hosting, and coordinates the interaction with the operating system, including loading and starting of components, signal trapping (opens new window) 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 (opens new window)
  • 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

Name Description Type
name Name of the process (used for logging) String
block Block used for registering components that will be run by the host Proc

# 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

Name Description Type
component_initiator A callable that starts consumers Callable
Name Optional 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 (opens new window).

# Recording or Reporting Errors

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

record_error(&blk)

Parameters

Name Description Type
blk A block that is executed when an unhandled error is raised to the level of the component host Proc
ComponentHost.start(component_name) do |host|
  host.record_error do |error|
    SomeErrorReporter.(error)
  end
end

# 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.

DANGER

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.

# Startup Output

When the component host starts, it will print information about the Ruby build that's running the component, environment variables that control various parts of the toolkit, as well as information about the components, consumers, streams, and handlers that are being hosted.

ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-darwin19]

Environment Variables:
  ENTITY_CACHE_SCOPE: exclusive
  MESSAGE_STORE_SETTINGS_PATH: (not set)
  POLL_INTERVAL_MILLISECONDS: (not set)
  HANDLE_STRICT: (not set)
  LOG_LEVEL: (not set)
  LOG_TAGS: (not set)
  STARTUP_INFO: (not set)
  ENV_VAR_INFO: (not set)

Host: account-component

  Component: AccountComponent::Start (Name: (none))

    Consumer: AccountComponent::Consumers::Commands
      Category: account:command
      Position: 5
      Identifier: (none)
      Correlation: (none)
      Position Stream: account:command+position

      Handlers:
        Handler: AccountComponent::Handlers::Commands
          Messages: Open, Close, Deposit, Withdraw

    Consumer: AccountComponent::Consumers::Commands::Transactions
      Category: accountTransaction
      Position: 0
      Identifier: (none)
      Correlation: (none)
      Position Stream: accountTransaction:position

      Handlers:
        Handler: AccountComponent::Handlers::Commands::Transactions
          Messages: Deposit, Withdraw

Host running: account-component
Process ID: 11111

Note: The above example output is taken from the account component example project: https://github.com/eventide-examples/account-component (opens new window)

# The Included Startup Info

The startup info includes details about components, consumers, streams, handlers, and messages, as well as environment variables used to override Eventide's runtime options, and the Ruby runtime.

# Component Information

The component information includes:

The consumer's info also includes:

The consumer's info also may optionally include:

# Environment Variable Information

The information printed when a service is starting also includes the environment variables that override some of Eventide's default behaviors.

The environment variable information includes:

# Controlling the Component Startup Information Output

The component startup information output is controlled by the STARTUP_INFO environment variable.

The variable's default value is "on".

To deactivate the printing of the component startup information, set the STARTUP_INFO to "off".

STARTUP_INFO=off start_service.rb

# Controlling the Environment Variable Information Output

The environment variable information output is controlled by the ENV_VAR_INFO environment variable.

The variable's default value is "on".

To deactivate the printing of the environment variables list, set the ENV_VAR_INFO to "off".

ENV_VAR_INFO=off start_service.rb

# Log Tags

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

Tag Description
component_host Applied to all log messages recorded inside the ComponentHost namespace

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

Tag Description
component_host Applied to messages that pertain to the component host
actor Applied to messages recorded by actors (opens new window)
lifecycle Applied to messages that pertain to the lifecycle of a actors
crash Applied to messages recorded while the host is terminating due to an error
signal Applied to messages recorded when handling operating system signals
start Applied to messages recorded when starting the component host or an actor
stop Applied to messages recorded when stopping an actor

See the logging user guide for more on log tags.