-
Notifications
You must be signed in to change notification settings - Fork 47
Services
Internal to Greengrass Nucleus each Greengrass component is implemented by the GreengrassService class, so internally they are known as "services" rather than components. I'll try and be consistent and clear when talking about components versus services.
GreengrassService class is the base class for all services including external components, internal services (ex: DeploymentService), and lambdas.
GreengrassService does not implement the state machine for actually moving the service between lifecycle states, it instead is the interface to request state changes and implements the state logic like install
or shutdown
. The state machine is separated out in the Lifecycle class.
When created, a GreengrassService begins by identifying its dependencies and configuring a callback for when the list dependencies changes. The service then identifies any change to the list of dependencies. For any removed dependency, the dependency state listener is removed. All remaining existing or new dependencies are then configured with a dependency state listener. A dependency state listener will receive state change events for all services, identify if it is the dependency service we're interested in, and then restart this service if the dependency service is not in a good state and this service is either currently running or starting up. If all dependencies are in a healthy state, then the state listener will also send a notification to the dependencyReadyLock
which will unblock the lifecycle thread which may be waiting for dependencies to be ready prior to starting this service.
After loading dependencies in the constructor, the dependency injector will call postInject
where we initialize the lifecycle. This starts the lifecycle thread running in preparation to get the service running.
Each service has a set of lifecycle commands: bootstrap()
, install()
, startup()
, shutdown()
, and close()
.
bootstrap()
will be executed when the Nucleus performs a deployment when isBootstrapRequired(Map<String, Object>)
is true
for this service and the Nucleus enters the BOOTSTRAP
phase where no other services are running.
Each service also has some query methods which services may choose to override: isBootstrapRequired(Map<String, Object>)
, shouldAutoStart()
, and isBuiltin()
.
Lifecycle implements the state machine for services. There is one instance per service and each instance runs its own thread. This lifecycle thread executes the state machine by identifying what state the service is currently in, and what state it wants to get to, if it isn't already in the desired state.
To move between states, lifecycle tracks a list of desired states so that it can perform more complicated actions like restarting which require going through multiple states. In the restart case, the desired states would be FINISHED
, RUNNING
so that the service first needs to get to the finished state where it is no longer running, then get into the running state again.
The lifecycle thread will block on the stateEventQueue
waiting for events such as a notification that we'd like to move the service to a different state.
Services can have the following states: STATELESS
(this state is never used), NEW
, INSTALLED
, STARTING
, RUNNING
, STOPPING
, ERRORED
, BROKEN
, FINISHED
. A normal service would start as NEW
, then INSTALLED
, STARTING
, RUNNING
, FINISHED
.
The BROKEN
state means that Greengrass is giving up on restarting the service due to the service erroring 3 times within 1 hour. There is no way to opt out of this behavior. A service will get out of BROKEN
if Greengrass restarts or the service moves itself to NEW
(reinstallation). An external service will reinstall itself when the version, install script, runwith, or resource limits change or when any other lifecycle changes and the service is in BROKEN
state.