Dependent Jobs #

Dependent jobs are a Pragma Engine structure that enable concurrent processing across services at moments when there are dependencies between processed data.

Dependent jobs can be used to manage the following conditions:

  • processing that can occur concurrently
  • processing that has a dependency on other processing
  • the need to collate data from multiple systems to be sent to the game client

Using dependent jobs #

There are two primary cases for dependent jobs:

  • Login Data Plugin
  • custom service plugins

Login Data Plugin #

Another place where data must be collected from multiple sources and sent to the client is on player login. The Login Data Plugin defines jobs that should be executed whenever a player logs in, and by default collects a player’s inventory data. Authoring additional dependent jobs can handle new capabilities, such as granting players rewards on login.

Custom Service Plugins #

Dependent jobs can be authored to pull custom data from a custom service. For more information, visit the custom services page.

Authoring dependent jobs #

A dependent job is composed of two main pieces:

  • dependenciesMet: a definition of dependencies that gate the execution of the job
  • makeRequest the job’s work

These are reflected in the method signature of the abstract class DependentJob:

protected abstract fun dependenciesMet(context: DependentJobContext): Boolean
protected abstract suspend fun makeRequest(context: DependentJobContext, mutable: ThreadsafeMutable<T>)

Dependencies #

When the method dependenciesMet returns true, then the dependent job is no longer gated and makeRequest will be called on the DependentJob.

To ascertain if dependencies are met, the dependent job uses DependentJobContext, which is passed into both methods. This is a cache of data types that have been processed.

Example

Here is a example of dependenciesMet() checking that that an InventoryRpc.GetLoginDataV1Response has already been proceesed by the appropriate dependent job. In this case, the work of the dependent job only executes once GetLoginDataV1Response has been added to the DependentJobContext object.

    override fun dependenciesMet(context: DependentJobContext): Boolean {
        return context.hasResult(InventoryRpc.GetLoginDataV1Response::class)
    }

Request #

Once a dependent job has determined that its dependencies are met, it can execute its work. The intent is to enable service requests to gather and collate data.

If another job depends on this data, add the response to the DependentJobContext using the function DependentJobContext.setResult. The response must be added so other dependent jobs can check for this response in its dependenciesMet() implementation.

Example

Here is an example of gathering inventory data for the GameDataService.GetLoginData response:

    override suspend fun makeRequest(context: DependentJobContext, mutable: ThreadsafeMutable<GameDataRpc.GetLoginDataV1Response.Builder>) {
        val pragmaResult = service.requestRpc(
            InventoryRpc.GetLoginDataServiceV2Request.newBuilder().setPlayerId(playerId.toFixed128()).build(),
            InventoryRpc.GetLoginDataServiceV2Response::class
        )

        pragmaResult.onSuccess { response ->
            mutable.write { it.loginDataBuilder.apply(LoginDataWrapper(response)) }
            context.setResult(result.response) 
        }
        pragmaResult.onFailure { serviceError -> service.logger.error("Error getLoginDataV1 playerId: {} for {}", playerId, serviceError) }
    }