How can I host services in the Golem network?
Golem allows you to launch and control interactive services. Contrary to batch processing tasks - which execute certain computations and finish once the results are ready - a service is, in general Golem terms, a process which runs under the direct control of Provider, based on the Agreement with a Requestor, and responds to requests (passed either via Golem network, or totally outside of Golem network's visibility), until it is explicitly stopped (usually by a Requestor).
In the Golem service model, the Requestor Agent application specifies the service which is to be instantiated and then controls the service instance throughout its lifecycle in the Golem network.
Our Services API provides an abstraction over Golem low-level APIs, which is aimed at making the building of service-oriented applications straightforward for a developer. The abstraction is based on a logical concept of a Service, in other words, an entity that implements the logic of a service application, and which, from Requestor's perspective, follows a certain sequence of states:
Transitions from one state to another take place as a result of certain events. The events may be triggered by a Requestor (RunService), Provider (AgreementTerminated), or may be a result of an external phenomenon (like errors of varying nature). Golem SDK's service programming model allows the developer to specify logic that is to be executed in subsequent "active" states of the Service's lifecycle (
Stopping). The HL API controls the transitions between states and hides the "plumbing" of Golem mechanics so that the developer can focus on their service's details.
The developer of a Golem service application needs to follow a certain pattern to implement fundamental aspects of service definition and control. A Service application includes an ExeUnit running on the Provider node, and a Requestor exercising control over that ExeUnit via Golem APIs. The ExeUnit can be eg. a VM hosting a specific payload application, or a custom ExeUnit controller/wrapper which integrates a third-party service software with the Golem ecosystem. In any case, the Service provisioned on the Golem network will require certain aspects to be specified in the Requestor Agent application.
In order to define a Golem Service, the developer must create a class/object to indicate the fundamental aspects of the Service to be provisioned. The class must include methods responsible for payload specification (the details of the Demand indicating eg. the ExeUnit/runtime to be sought on the market), and logic to be executed in "active" states of the service lifecycle.
The code snippets below are illustrating a very basic service (a
SimpleService), hosted in a standard Golem VM runtime, where service "requests" are the shell commands executed repeatedly on the VM while the service is running.
The Requestor Agent app must define the "payload" - the detailed specification of the service which is to be provisioned. This specification is then wrapped in a
Demand structure and published on the Golem market.
class SimpleService(Service):@staticmethodasync def get_payload():return await vm.repo(image_hash="8b11df59f84358d47fc6776d0bb7290b0054c15ded2d6f54cf634488",min_mem_gib=0.5,min_storage_gib=2.0,)...
A HL API library controls all aspects of Provider finding, negotiations, and instantiating an Activity. The app needs to indicate the actions to be executed in subsequent "active" states of the Service's lifecycle.
Once a Golem activity starts and the Service instance begins its life, the Requestor Agent must indicate all actions to be executed in order to set up the service.
...async def start(self):self._ctx.run("/golem/run/simulate_observations_ctl.py", "--start")yield self._ctx.commit()...
start() method follows a 'work generator' pattern. It uses
WorkContext instance (accessed via
_ctx) to build a sequence of actions which then gets returned to the service execution engine to be asynchronously relayed to the Provider's runtime. Please take a look at the methods provided by the
WorkContext to get familiar with the possible work steps that can be performed via Golem APIs:
start() sequence of actions is executed only once in Service's lifecycle and must result either with success, or indication of failure, in which case the Service immediately moves to
Once started, the Service moves in Running mode - a normal state of operation. In this state, the Requestor Agent may for example; monitor & control the service (either via Golem APIs or contacting the service directly via other means).
...async def run(self):while True:await asyncio.sleep(10)self._ctx.run("/golem/run/simple_service.py", "--stats") # idx 0future_results = yield self._ctx.commit()results = await future_resultsstats = results.stdout.strip()print(stats)...
Note that the Requestor Agent may at some point decide to end the service while it is in
Running state - this ends the actions specified for
Running state and triggers the transition to
In case the service gets halted, either by Requestor's decision or due to Provider-triggered termination, provided the activity (and thus, the attached
WorkContext ) is still available, the Service moves to a
Stopping state, in which a Requestor Agent still may have an ability to e.g. recover some artifacts from the service instance, or perform some general clean sweep.
...async def shutdown(self):self._ctx.run("/golem/run/simulate_observations_ctl.py", "--stop")yield self._ctx.commit()...
Note that the
Stopping actions are executed only once in Service's lifecycle.
Once a service specification class/object is defined, the service can be provisioned on the Golem network. This is done via a
Golem execution processor object, which hides all the technical nuances of market interaction, activity tracking, and service state lifecycle:
async with Golem(budget=1.0,subnet_tag=subnet_tag,driver=driver,network=network,event_consumer=log_summary(log_event_repr),) as golem:cluster = await golem.run_service(SimpleService,num_instances=NUM_INSTANCES,expiration=datetime.now(timezone.utc) + timedelta(minutes=15))...
Golem call returns a
Cluster of (in this case)
SimpleService objects, each representing an instance of the service, as provisioned on the Golem network. The
Cluster can be used to control the state of the services (e.g. to stop services when required).
This is all it takees to build a Requestor Agent for rudimentary VM-based service. Soon, we'll show you a bit more sophisticated service examples, eg. including custom runtimes. Stay tuned!
For now, you may wish to jump to:
Have fun exploring the new Service API!