18. External Sessions

Previous chapter

Next chapter

GemStone/S 64 Bit incorporates a number of classes that facilitate spawning and managing external sessions; this chapter describes these classes and how to use them.

External sessions allow you to execute Smalltalk code in separate Gems, which may run on different servers and log in as different users to different repositories. This allows you do to things such as partitioning work among multiple gems or managing separate repositories.

Operations to create and communicate with the external sessions use the Foreign Function Interface (FFI) to access the GCI libraries, except on AIX, which uses GciInterface primitives. GciLibrary provides interface methods for all the GemBuilder for C functions.

Specifying NRS with GsNetworkResourceString
describes how to programmatically define NRS Strings

Using External Sessions
How to create and use external sessions

18.1 Specifying NRS with GsNetworkResourceString

GemStone uses a Network resource string, or NRS, to specify the details for the gem and stone on login. NRS strings are also used for other purposes and include a number of features; the NRS syntax is documented in the System Administration Guide, appendix C.

While you may compose strings in NRS syntax for your external session logins, the new class GsNetworkResourceString provides a way to compose NRS strings from the significant elements.

This class includes parameters that are meaningful for both Stone and Gem NRS strings, which may have a different meaning in the gem vs. in the stone, and ones which apply to one or the other but not to both.

Gem NRS methods

The following GsNetworkResourceString class methods return the NRS for a gem, using defaults as needed:

gemNRS
gemNRSForNetLDI: nameOrPort
gemNRSForNetLDI: nameOrPort onHost: gemhostname
gemNRSForNetLDI: nameOrPort onHost: gemhostname gemService: customGemService

nameOrPort specifies the netldi name, or the port of the netldi on the stone’s host. If not provided, gs64ldi is used.

gemhostname specified the name of the host on which the gem will run. If a variant without this argument is used, it will default to the stone’s host.

customGemService is the name of the gem service (script). This is normally gemnetobject, but may be gemnetdebug or a customized script.

Stone NRS methods

The following GsNetworkResourceString class methods return the NRS for a gem, using defaults as needed:

stoneNRS
stoneNRSForStoneName: aStoneName
stoneNRSForStoneName: aStoneName onHost: stoneHostName

aStoneName is the name of the stone that you will log into. If a variant without this argument is used, it defaults to gs64stone.

stonehostname specifies the name of the host on which the stone is running. If a variant without this argument is used, it will default to localhost.

Example 18.1 Example NRS

With the Stone on a machine named santiam, to run with a Gem on the Stone’s node , the Stone and Gem NRS could be defined as follows:

myStoneNRS := GsNetworkResourceString 
	stoneNRSForStoneName: 'gs64stone' 
	onHost: '	santiam.gemtalksystems.com'.
myGemNRS := GsNetworkResourceString 
	gemNRSForNetldi: 'gs64ldi' 
	onHost: 'santiam.gemtalksystems.com'
 

GsNetworkResourceString direct protocol

The following instance methods set NRS parameters directly. You may either create an instance of GsNetworkResourceString using the above methods, and send these messages to further specify the NRS; or you may create a new instance of GsNetworkResourceString and construct it using these and other methods.

log:
Set the name of the log file for the Gem service. Optional; applies for the Gem’s NRS.

temporaryObjectCacheSize:
Specify the size of the temporary object cache of the new gem. Optional; applies for the Gem’s NRS.

dir:
Applies when starting a gem session. Set the default directory for the Gem. Optional; applies for the Gem’s NRS.

authorization:
Sets the host UNIX user name and password. For example, ’user@password’. Applies for the Gem’s NRS, if required by the NetLDI mode.

netldi:
Set the name or port of the netldi that will be used to service the request. Applies for the Gem’s NRS, if not using the default netldi name.

node:
For a stone, sets the node that the stone is running on; when starting a gem session, the node that the gem process will be run on.

body:
For a stone, the name of the stone. For a gem, the name of the gem service. Gem services may be gemnetobject or gemnetdebug, or a custom gem service. Gem service arguments such as gemnetobject -C can be included here.

18.2 Using External Sessions

To use an external session, you must create an instance of GsExternalSession, set the appropriate login parameters, and login, which creates the external gem session.

After you have executed operations on the external gem, you must logout, to ensure the external gem is terminated and does not continue to use resources.

You cannot persist instances of GsExternalSession in the repository.

Setup the External Session

The login parameters you configure are the same as when logging in via topaz or other interfaces: the Stone’s Network Resource String (NRS), the Gem’s NRS, and the userId and password that the external session will login as. If your login requires host username and host password, these are also provided as part of the NRS arguments.

The Stone and Gem NRS may be provided as instances of the class GsNetworkResourceString, described in the previous section, or as strings using GemStone’s standard NRS syntax.

Creating the External Session

To create the external session, create an instance and specify instances of GsNetworkResourceString or NRS strings. The following examples show equivalents using GsNetworkResourceString and NRS strings.

With GsNetworkResourceString

Note that this uses the instances of GsNetworkResourceString defined above.

GsExternalSession 
	gemNRS: myGemNRS
	stoneNRS: myStoneNRS
	username: 'DataCurator'
	password: 'swordfish'
Using NRS strings
GsExternalSession
	gemNRS: 
       '!@santiam.gemtalksystems.com#netldi:gs64ldi!gemnetobject'
	stoneNRS: '!@santiam.gemtalksystems.com!gs64stone'
	username: 'DataCurator'
	password: 'swordfish'
Default

GsExternalSession class >> newDefault creates a new instance of GsExternalSession based on the login parameters of the current session. If you are logging in as the same UserProfile in the same repository, this initializes most of the information that is needed. Based on his, you may refine parameters using instances methods on GsExternalSession. For example:

GsExternalSession newDefault
	username: 'GcUser';
	password: 'swordfish';
	yourself

Log in the External Session

To login, send #login to the configured GsExternalSession:

myGsExternalSession login.

Login creates a gem session that is logged in and in transaction in the specified stone, either the same stone as the calling session or a different stone. If the external gem is logged into a stone that is in active use, you must manage the gem appropriately to avoid creating a commit record backlog in that stone; avoid leaving external gems logged in and idle, and ensure that the code you execute commit or aborts regularly.

To logout, send #logout to the logged-in GsExternalSession:

myGsExternalSession logout.

Executing Code

Code to be executed by the external session can be passed as strings or blocks. These can be executed synchronously or asynchronously.

Code in Strings

To synchronously execute code contained in a string, use the method executeString:.

For example:

myGsExternalSession executeString: 
'SystemRepository fullBackupTo: ''/backups/gs/bkup15-06-23.dat'''. 
Code in Blocks

What is actually sent to the remote session is always in the form of a String, but methods are provided that accept blocks containing the code to execute in the remote session. The source strings for these block will be passed to the remote session. This allows Smalltalk tools to manage the source, detect senders, and so on, which is not possible with strings.

To use blocks, the blocks must be able to compile in both the calling session and the remote session in which you intend them to execute, although the block’s code is not necessarily meaningful in the calling session. Any variable resolution, etc. in the blocks will be resolved again in the environment of the remote session when the block is compiled after being transmitted as a string, and if the variables cannot be resolved in the remote session, it will result in an error.

Code in block can also be executed synchronously or asynchronously.

To synchronously execute code contained in a block, use:

executeBlock: aNoArgBlock

executeBlock: aOneArgBlock with: aValue

executeBlock: aTwoArgBlock with: aValue with: anotherValue

executeBlock: aBlock withArguments: aCollectionOfValues
These methods execute the source code contained in the given block, and return the result of executing that code.

When passing arguments to the block, the arguments values must be objects for which the printString allows the correct object state to be recreated in the remote session. This is true for all objects, including specials, strings, integers and floats; use caution to avoid unexpected conversion or loss of information as well as errors.

Return Values

After code is executed in the remote session, the result is returned to the calling session.

If the result of the expression is a special (Character, Boolean, SmallInteger, SmallFloat, etc.), or a String, Symbol, or ByteArray, the results are converted into the appropriate object in the calling Gem.

When the result is not a special, then the OOP of the result is placed in the ExportSet of the remote session. See Important caution on Export Set of remote session.

Expressions that return another type of object will return an Array containing the OOP of the result. This should be avoided, except when performing additional remote operations on returned OOPs. The returned OOP is for the value of the result in the remote session, which may not exist or be resolvable in the calling session; and OOP lookup has an inherent risk of unexpected results.

Since the evaluation is done in a separate Gem process, any transient changes in the remote Gem are not visible in the calling Gem. In order for persistent changes in the remote Gem to be visible to the calling Gem, the remote Gem must commit the changes, and the calling Gem must abort.

Asynchronous Execution

The executeString: and executeBlock: methods block the calling session until execution completes. To execute the remote code asynchronously and return control immediately to the calling session, the following equivalent methods are available:

forkString: aString
forkBlock: aNoArgBlock
forkBlock: aOneArgBlock with: aValue
forkBlock: aTwoArgBlock with: aValue with: anotherValue

When you execute asynchronously, an external call is in progress, and the methods you can invoke on the remote session are limited:

isResultAvailable
Check whether the current call in progress has finished and save the result if it has.

lastResult
Answer the result received when the last isResultAvailable answered true, which includes after a waitForResult operation completed.

waitForResult
Wait for the external Gem to complete the current operation.

waitForResultForSeconds: numSeconds
Wait up to numSeconds seconds for the external Gem to complete the current operation.

waitForResultForSeconds: numSeconds otherwise: aBlock
Wait up to numSeconds seconds for the external Gem to complete the current operation. If the operation does not complete within that time, answer the result of evaluating aBlock.

Operations on remote objects

If you perform a remote operation that returns an OOP, you can send specific selectors to that remote object by OOP.

send: selector to: anOop
Send the given selector to the object in the external session with the OOP anOop.

send: selector to: anOop withArguments: anArrayOfValues
Send the given selector to the object represented by the given OOP, which is an OOP in the external session, and pass the Array of arguments.

The OOPs of the arguments are passed to the remote session. These arguments must be specials, or persistent objects that exist on both the calling and remote sessions, otherwise it will result in an error.

Managing Remote Sessions

Managing transaction state

Management of transaction state in the remote gem can generally be done by executing code on the remote gem. The following methods are provided for convenience.

GsExternalSession >> abort
GsExternalSession >> commit

Logging

Login and logout will output messages to stdout for the session that created the GsExternalSession; either the RPC Gem log, or the linked topaz session. You may control the location of this logging, or suppress these messages using GsExternalSession >> suppressLogging. However, the regular GCI login message is sent by the GCI layer, and is not affected by image-level logging control.

Breaking remote execution

You can break execution on the remote session using

GsExternalSession >> softbreak
GsExternalSession >> hardbreak

Important caution on Export Set of remote session

For objects other than specials (Integers, Characters, etc.) that are returned by the remote Gem, the remote Gem adds these objects to its export set. This includes Strings and other byte collections, Exceptions returned by the external session, and other objects that are returned as OOPs. These OOPs remain in the export set of the remote gem, and will not be garbage collected, until that gem is logged out. These OOPS can be removed manually from the export set using Hidden Set protocol.

Although Strings and similar byte-format results and exceptions are converted into new String (or appropriate) instances in the calling Gem (with a new OOP), the OOP of the original String on the remote Gem remains in the external Gem’s export set.

Exceptions

The class GciError and GciLegacyError are provided to represent errors during remote execution. If the code being executed on the remote session encounters an exception, this is raised as a GciError in the calling session, or a GciLegacyError if using GsLegacyExternalSession.

Since remote debugging is not possible with this interface, the stack of the error is included with the error description.

For example, given the following code which triggers an error on the remote session:

result := [myGsExternalSession executeString: '1/0']
	on: GciError
	do: [:ex | ex description].

The result in the calling session will be:

GciError: a ZeroDivide occurred (error 2026),
reason:numErrIntDivisionByZero, attempt to divide 1 by zero
1 AbstractException >> signal @1 line 1
    receiver a ZeroDivide occurred (error 2026),
reason:numErrIntDivisionByZero, attempt to divide 1 by zero
2 Number >> _errorDivideByZero @5 line 6
    receiver 1
3 SmallInteger >> / @5 line 7
    receiver 1
    aNumber 0
4 Executed Code  @1 line 1
    receiver nil
5 GsNMethod class >> _gsReturnToC @1 line 1
    receiver nil
 

Previous chapter

Next chapter