Loader agent protocol - myaut/tsload GitHub Wiki

Describes loader agent API in com.vperflab.tsserver Updated to version 0.2

UML sequence diagram for Loader agent is shown here: LoaderAgentSequence.png

NOTE: various annotation helpers are cleaned to reduce this article

Comments on Server-Agent interaction

Running workload constists of X steps:

hello()

Loader agent is started on target system. It opens JSON-TS connection to server and then invokes hello():

def hello(client: TSClient[TSLoadClient], info: TSHostInfo) : TSHelloResponse

It provides various information about host (hostname, number of processors, amount of memory and so on) in info parameter. If server successfully registers loader agent, it returns id of agent from database in TSHelloResponse. Agent id currently is not used in further communication.

getWorkloadTypes()

Server asks for loader configuration, list of modules that shipped with agent in particular.

def getWorkloadTypes : TSWorkloadTypeList

Agent returns Map(wltypeName -> TSWorkloadType):

class TSWorkloadType extends TSObject {
  var module: String = _
  var path: String = _
  
  @TSObjContainer(elementClass = classOf[TSWorkloadParamInfo])
  var params: Map[String, TSWorkloadParamInfo] = _
}

class TSWorkloadTypeList extends TSObject {
  @TSObjContainer(elementClass = classOf[TSWorkloadType])
  var wltypes: Map[String, TSWorkloadType] = _
}

For each parameter agent defines:

  • name (as key in TSSingleModuleInfo.params map)
  • description
  • type (string, integer, etc.). Depending on type, one of TSWorkloadParamInfo derivatives will be instantiated by JSON-TS deserializator:
    • TSWLParamBooleanInfo
    • TSWLParamIntegerInfo
    • TSWLParamFloatInfo
    • TSWLParamSizeInfo
    • TSWLParamStringInfo
    • TSWLParamStringSetInfo
  • default - default value for param
  • range - minimum and maximum for numbers, maximum length for string
  • meta-type - additional info that determines what type of data this param representing (i.e. integer may represent time) - not implented yet

NOTE: default and range params are optional, so it can be nil

configureWorkload()

User desides to start expreriment. Therefore, server sends configure_workload command to client:

def configureWorkload(workloadName: String, workloadParams: TSWorkload)

Where workloadParams:

class TSWorkload extends TSObject {
  var module: String = _
  var threadpool: String = _
  var params: Map[String, AnyRef] = _
}
  • module - name of module which will configure/run workload
  • threadpool - name of threadpool that will execute workload. For now only [DEFAULT] is valid
  • params - Map(paramName -> paramValue). **NOTE: ** Due to bug (?) in JSON-TS, parameter values should be Java-typed (not Scala-typed)

After that, loader agent spawns configuration thread and prepares to configure workload.

workloadStatus()

During configuration of workload, agent notifies server on configuration status.

def workloadStatus(client: TSClient[TSLoadClient], status: TSWorkloadStatus)

Where status is:

object TSWorkloadStatusCode {
  val CONFIGURING = 1
  val SUCCESS = 2
  val FAIL = 3
  val FINISHED = 4
  val RUNNING = 5
}
class TSWorkloadStatus extends TSObject {
  var workload: String = _
  var status: Long = _
  var progress: Long = _
  var message: String = _
}
  • workload - name of reported workload
  • progress - number charasteristic of progress
  • status - status code for workload
    • CONFIGURING - workload is configuring
    • SUCCESS - workload configuration is successfully finished. progress = 100
    • FAIL - failure encountered during workload configuration or execution. progress = -1
    • FINISHED - workload execution is finished
    • RUNNING - workload execution is currently running. progress = #id of current step
  • message - status message

NOTE: since agent 0.2, done renamed to progress

startWorkload()

When server receives SUCCESS workload status message, it may start workload. Workload are started not immediately start_workload command received because when multiple systems are involved in experiment, network latency may cause mismatch between start times of workload. Server transfers startTime in nanoseconds from UNIX epoch:

def startWorkload(workloadName: String, startTime: Long) 

provideStep()

Server feeds agent with step data.

def provideStep(workloadName: String, stepId: Long, numRequests: Long) : TSProvideStepResult

If step queue on agent is full, server will return result != 0:

class TSProvideStepResult extends TSObject {
  var result: Long = _
}

requestsReport()

When agent workers are finished execution of step, they report agent statistics per each request to server:

def requestsReport(client: TSClient[TSLoadClient], report: TSRequestReport)

Where report is:

object TSRequestFlag {
  val STARTED  = 0x01
  val SUCCESS  = 0x02
  val ONTIME   = 0x04
  val FINISHED = 0x08
}
class TSRequest extends TSObject {
  var workload_name: String = _  
    
  var step: Long = _
  var request: Long = _
  var thread: Long = _
  
  var start: Long = _
  var end: Long = _
  
  var flags: Long = _
}
class TSRequestReport extends TSObject {
  var requests: List[TSRequest] = _
}
  • workload_name - name of workload
  • step - id of step
  • request - id of request in step (0 .. numRequests)
  • thread - id of thread executed request
  • start, end - start and finish time of request execution (in ns, starting from UNIX epoch)
  • flags
    • STARTED - request execution is started
    • SUCCESS - request successfully executed
    • ONTIME - request finished execution before quantum was exhausted
    • FINISHED - requests execution is finished

workloadStatus()

When no steps are left on steps queue for workload, workload is destroyed and workload_status message is sent to server.

Threadpool API

getThreadPools()

Returns available thread pools and dispatchers

def getThreadPools : TSThreadPoolList

class TSThreadPool extends TSObject {
  var num_threads: Long = _
  var quantum: Long = _
  
  var wl_count: Long = _
  
  var disp_name: String = _
  
  @TSObjContainer(elementClass = classOf[String])
  var wl_list: List[String] = _
}

class TSThreadPoolList extends TSObject {
  @TSObjContainer(elementClass = classOf[String])
  var dispatchers: List[String] = _
  
  @TSObjContainer(elementClass = classOf[TSThreadPool])
  var threadpools: Map[String, TSThreadPool] = _
}
  • num_threads - number of threads configured in threadpool
  • quantum - thread pool quantum in nanoseconds
  • wl_count - number of workloads attached to threadpool
  • disp_name - name of dispatcher used by threadpool
  • wl_list - list of workload names

createThreadPool()

def createThreadPool(threadpoolName: String, numThreads: Long, quantum: Long, dispName: String)

destroyThreadPool()

def destroyThreadPool(threadpoolName: String)

Examples

Configuring workload:

import java.lang.{Boolean => JBoolean, Integer => JInteger, Double => JDouble}

def configureTestWorkload(client: TSClient[TSLoadClient]) {
    var workload = new TSWorkload()
    
    workload.module = "dummy"
    workload.threadpool = "[DEFAULT]"
    workload.params = Map("filesize" -> (16777216 : JInteger),
                          "blocksize" -> (4096 : JInteger),
                          "path" -> "/tmp/testfile",
                          "test" -> "read",
                          "sparse" -> (false : JBoolean))
        
    var workloads = new TSWorkloadList(Map("test" -> workload))
        
    client.getInterface.configureWorkloads(workloads)
}

Start workload:

def startTestWorkload(client: TSClient[TSLoadClient]) {
    /*Add five seconds and convert ms to ns*/
    var startTime: Long = (System.currentTimeMillis() + 5000) * 1000 * 1000

    client.getInterface.startWorkloads(new TSWorkloadStartList(Map("test" -> startTime)));

    /*Create 12 steps, 10 requests per step*/
    var testSteps = new TSWorkloadSteps()
    
    testSteps.workloadName = "test"
    testSteps.steps = (1 to 12).map(i => new TSWorkloadStep(i, 10)).toList
    
    client.getInterface.provideSteps(testSteps)
}