TaskIt integration

January 4, 2020 ยท View on GitHub

Tarantalk async API feature can be leveragted much by integrating with TaskIt. You can orchestrate async requests with combinable #future sends.

Installation

Please load 'Tarantalk-TaskIt' package for enabling TaskIt integration, that is included in the 'extra' package group.

Metacello new
   baseline: 'Tarantalk';
   repository: 'github://mumez/Tarantalk/repository';
   load: #('extra').

TaskIt is required and will be installed automatically.

Using future sends

First, let's look back at the regular async API. By default, Tarantalk accepts async requests callback by #ifDone:ifFailed:.

talk := TrTarantalk connect: 'taran:talk@localhost:3301'.
talk executeSql: 'drop table if exists programming_languages'.
talk executeSql: 'create table programming_languages (id integer primary key, name varchar(100), description varchar(100))'.

talk executeSql: 'insert into programming_languages values (?, ?, ?)' values: {1. 'Smalltalk'. 'cool'}.
talk executeSql: 'insert into programming_languages values (?, ?, ?)' values: {2. 'Lua'. 'hot'}.

(talk asyncExecuteSql: 'select * from programming_languages')
	ifDone: [:ret | Transcript cr; show: ret ] ifFailed: [:error | error pass].

However, in #ifDone:ifFailed:, you can only register one success callback and one failure callback respectively.

In #future sends, you can register multiple success/failure callbacks.

future := (talk asyncExecuteSql: 'select * from programming_languages') future.
future
	onSuccessDo: [:ret | Transcript cr; show: ret ];
	onSuccessDo: [:ret | ret metadata inspect ];
	onFailureDo: [:error | error signal];
	onFailureDo: [:error | error inspect].

Combining future sends

TaskIt provides future combinators, so that you can process complex async results in a fluent way.

#andThen: is the most basic one. You can chain async executions sequentially.

((talk asyncExecuteSql: 'select * from programming_languages where name = ?' values: {'Smalltalk'}) future
	andThen: [:rows | 1 seconds wait. Transcript cr; show: rows]) andThen: [:v | Transcript cr; show: v first]. "eventually executed"
Transcript cr; show: (talk executeSql: 'select * from programming_languages'). "immediately executed"

In this case, you will see the last sync query result immediately. One second later, async query result will be shown on Transcript.

#(#(1 'Smalltalk' 'cool') #(2 'Lua' 'hot'))
#(#(1 'Smalltalk' 'cool'))
#(1 'Smalltalk' 'cool')

The following is a complicated example. The results of the two asynchronous queries are collected (mapped) by #collect: and combined into one future by #zip:.

(((talk asyncExecuteSql: 'select * from programming_languages where name = ?' values: {'Smalltalk'}) future collect: [:rows | rows first])
	zip: ((talk asyncExecuteSql: 'select * from programming_languages where name = ?' values: {'Lua'}) future collect: [:rows | rows first]))
		andThen: [ :zippedRows | Transcript cr; show: (zippedRows collect: [:each | each second -> each third]) asDictionary ].

As a result, you will see a merged Dictionary in Transcript.

a Dictionary('Lua'->'hot' 'Smalltalk'->'cool' )

Setting task runners

TaskIt has the notion of Task Runners for customizing how scheduled tasks will be eventually executed.

Simply, you can specify the TaskRunner by passing the furure: argument. In our integration, TrTaskRunnerFactory provides a handly way for creating preset runners.

((talk asyncExecuteSql: 'select * from programming_languages') future: TrTaskRunnerFactory defaultCommonQueueWorkerPool)
	andThen: [ :ret | Transcript cr; show: ret ].

Actually, if you do not pass the argument, TrTaskRunnerFactory defaultCommonQueueWorkerPool instance will be used as a task runner by default. So, the following is the same as above:

((talk asyncExecuteSql: 'select * from programming_languages') future)
	andThen: [ :ret | Transcript cr; show: ret ].

The default setting can be overwritten for each Tarantalk connection basis by using `TrSettings>>#taskRunnerType:'.

talk settings taskRunnerType: #workerPool.
((talk asyncExecuteSql: 'select * from programming_languages') future)
	andThen: [ :ret | Transcript cr; show: ret ].

In this case, TrTaskRunnerFactory defaultWorkerPool instance is implicitly used in #future sends.

If you pass #newProcess as the argument:

talk settings taskRunnerType: #newProcess.

Then, a TKTNewProcessTaskRunner will be a default runner for this Tarantalk connection.

The currently supported symbols are:

  • #newProcess
  • #workerPool
  • #commonQueueWorkerPool
  • #default (same as the #commonQueueWorkerPool)