17. GemStone System Features

Previous chapter

Next chapter

This chapter describes some features and internal structures that provide specialized behavior.

These structures are intended for use by experienced GemStone programmers.

Hidden Sets
Describes HiddenSets, a non-persistent way to manage objects using bitmaps.

SessionTemps and access to Session State
Ways to keep session-temporary data available for the life of a session.

Shared Counters
Integer counters that can be shared between sessions. Both non-persistent and persistent counters are available.

Easily track errors, trace code and record objects in a multi-session environment.

17.1 Hidden Sets

GemStone’s internal bitmap structures that hold OOPs efficiently and with minimal memory demands are encapsulated by instances of GsBitmap. Specific internal bitmaps are accessible to the image via the hidden set API in System class.

For historic reasons, some repository scan methods are available that write results temporarily to a hidden set. These results can be converted to a GsBitmap for processing, with the advantages of avoiding overloading temporary object memory. While there are replacement methods that return GsBitmaps directly, you may still encounter methods that put the results in a hidden set, rather than returning a GsBitmap

For example, while the method allInstances: returns a GsBitmap, the equivalent method listInstancesToHiddenSet: puts the results of a listInstances operation into a specific hidden set (in this case, hidden set 1) The following code shows the call to this method, and how to use hidden set protocol to migrate each object:

Example 17.1

SystemRepository listInstancesToHiddenSet: MyClass.
bm := GsBitmap newForHiddenSet: (GsBitmap hiddenSetIdAsSymbol: 1).
[ bm size > 0 ] 
		| resultBatch |
		resultBatch := bm removeCount: 1024.
		resultBatch do: [:aMyClass | aMyClass migrate].
		System commitTransaction.

For more on working with instances of GsBitmap, see GsBitmap.

The specific set symbolic names that are accessible from GsBitmap are listed in the method GsBitmap class >> hiddenSetSpecifiers. Many of these are read-only.

Note that the method System >> HiddenSetSpecifiers lists integer indexes of hidden set IDs. This list is ordered differently than the symbolic names in GsBitmap specifiers; the index offsets are not interchangeable. You can look up a GsBitmap identifier based on a hidden set integer id using the method GsBitmap class >> hiddenSetIdAsSymbol:.

Sets still accessed via System methods

The following internal sets may be accessed using System methods and respond to some hidden set protocol:


The #NotifySet is System hidden set 25.

The #NotifySet has an interface in class System with methods in the Notification category; clearNotifySet, addToNotifySet:, etc.

ExportedDirtyObjs and TrackedDirtyObjs

#ExportedDirtyObjs is System hidden set 22.
#TrackedDirtyObjs is System hidden set 23.

The methods gciDirtyObjsInit and gciTrackedObjsInit enable dirty and tracked object sets, respectively. Use getAndClearGciDirtySet:into: to retrieve the results.

PureExportSet and GciTrackedObjs

#PureExportSet is System hidden set 39
#GciTrackedObjs is System hidden set 40.

Methods in class System, category ’Gci Set Support’ allow you to add and remove objects from these hidden sets. Note you must initialize the dirty set using gciDirtyObjsInit before adding or removing from GciTrackedObjs.

17.2 SessionTemps and access to Session State

Data in GemStone is either temporary or persistent. While most temporary data is only retained for as long as the method is executing, or until the session updates its commit record by committing or aborting, you may sometimes want data that is not persistent and not shared, so does not risk transaction conflicts, but remains unaffected by transaction status.

Session-specific data of this kind can be put into SessionTemps. SessionTemps current provides access to a kind of SymbolDictionary; elements in the SessionTemps dictionary remain until the session logs out or exits, are not affected by commit or abort, and are not visible outside of the session.

For example, if you wish to open a log file and leave it open:

SessionTemps current at: #Log put: 
(GsFile openAppend: 'myFile.log')

Actual code, of course, would do more error checking. To write to the file, use code similar to this:

(SessionTemps current at: #Log) 
nextPutAll: 'a message for the log file'.

Objects in SessionTemps use temporary object memory, and the objects cannot be removed from memory by in-memory garbage collection. While there is no limit on how much data can be stored in SessionTemps, if your session reaches the memory limit and exits, that data will be lost.


SessionTemps uses a slot in the internal Session State structure, which is primarily provided for use by the kernel. Access to customer-available SessionState slots is provided primarily for legacy uses, but may be useful depending on application requirements.

SessionState is accessed by integer index, with slots 1 to 1994 available for use. The SessionState array is variable size, and will grow as needed.

The following methods can be used to read and update SessionState:

System >> sessionStateAt: anIndex
System >> sessionStateAt: anIndex put: anObject
System >> sessionStateSize

17.3 Shared Counters

There are two types of Shared Counters available; AppStat Shared Counters and Persistent Shared Counters.

AppStat Shared Counters provide a way for sessions on the same shared cache to read and update a set of counters. These counters are stored in the shared cache and are not persistent across cache restart. They are not visible to sessions on different shared page caches - that is, a session on the stone’s cache and a session on a remote cache cannot access the same Shared Counters. Values are also not recoverable from tranlogs.

Persistent shared counters are stored in the repository, and are visible to all sessions on all shared caches. On repository recovery or restore, the values of persistent shared caches are restored.

AppStat Shared Counters

Shared counters allow multiple sessions on the same SPC to read and update a common counter value.

Shared counters are indexed from 0 to (System numSharedCounters - 1), which is set by the configuration parameter SHR_PAGE_CACHE_NUM_SHARED_COUNTERS. The default value for SHR_PAGE_CACHE_NUM_SHARED_COUNTERS is 1900. Each counter is protected by a unique spinlock. The index of the first counter is 0.

Shared counters may be set to any signed 64 bit integer value, in the range:
-263 (-9223372036854775808) to 263 - 1 (9223372036854775807)
If you increment or decrement so that the result would be outside the range of a signed 64-bit integer, the value will be set to the minimum or maximum; directly setting an out of range value will result in an error.

Shared counters are transient, that is, they do not persist across cache restart.

Shared counter values are recorded by statmonitor when using the -n option and recorded as AppStats.

The following methods may be used to read and update shared counters. For details, see the method comments in the image.

System class >> numSharedCounters
System class >> sharedCounter: index 
System class >> sharedCounter: index setValue: value
System class >> sharedCounter: index incrementBy: amount 
System class >> sharedCounter: index decrementBy: amount 
System class >> sharedCounter: index decrementBy: amount withFloor: floorValue
System class >> sharedCounterFetchValuesFrom: firstCounter to: lastCounter

Persistent Shared Counters

Persistent shared counters allow all sessions in a repository to read and update a set of counters. Persistent shared counters are globally visible to all sessions on all shared page caches.

There are 1536 persistent shared counters, numbered from 1 to 1536. The index of the first counter is 1.

Persistent shared counters may be set to any signed 64 bit integer value, in the range:
-263 (-9223372036854775808) to 263 - 1 (9223372036854775807)
No limit checks are done when incrementing or decrementing a counter. If you increment or decrement so that the result would be outside the range of a signed 64-bit integer, the value will “rollover” and the overflow bits will be lost. Directly setting an out of range value will result in an error.

Values of all persistent shared counters are stored in the repository and in tranlog records. They are persistent through Stone restart, and recovered on Stone crash, restore from backup, and restore from tranlog.

Persistent shared counters are independent of database transactions. Updates to counters are visible immediately and not affected by aborts.

Each update to a persistent shared counter causes a roundtrip to the Stone; but reading the value is handled by the gem (and the page server, if remote), and does not cause a roundtrip to the stone.

The following methods may be used to read and update persistent shared counters. For details, see the method comments in the image.

System class >> numberOfPersistentSharedCounters
System class >> persistentCounterAt: index put: value
System class >> persistentCounterAt: index 
System class >> persistentCounterAt: index incrementBy: amount
System class >> persistentCounterAt: index decrementBy: amount

17.4 GsEventLog

GsEventLog is a shared, reduced-conflict logging tool that allows multiple sessions to:

A GsEventLog class variable holds a instance of GsEventLog, which in turn holds a collection of log entries, which are instances of kinds of GsEventLogEntry. It is allowed for user applications to create custom subclasses of GsEventLogEntry.

Each event has a priority. Built in priorities are fatal, error, warning, info, debug, and trace; these are stored internally as integers.

The entries are stored in an instance of RcArray, allowing concurrent writes of log entries. Note that the order of elements is based on the order in which the commits occurred, while entry timestamps reflect the time at which the entry was created.

Adding events

GsEventLog may hold both application (user) events and system events. User entries can be added in two ways: class convenience methods such as logError:, logInfo:, etc., or by creating an instance of GsEventLogEntry and sending addToLog. A commit is required to make the log entry persistent.

System events should be added only by GemStone code. In this release, GemStone code does not write System events.

Querying and reporting

To create a string containing text representation of the entire contents, send

GsEventLog current report

To search for a subset of entries with particular attributes

(GsEventLog current entriesSatisfying: aBlock) report

Deleting events

GsEventLog is not reduced conflict for delete. It is recommended to lock the log using GsEventLog >> acquireGsEventLogLock. The lock is cleared automatically on commit.

You can clear all events using GsEventLog current deleteAllEntries. By querying for specific methods, you can delete those methods using deleteEntry: or deleteEntries:.

It is possible to restrict modifying or removing events. To do this, execute

GsEventLog entriesUnmodifiable 

After this is executed, new entries to the log are made invariant and the standard delete methods will not delete them. However, they are not protected from delete using private delete protocol.

System events are also protected from modification or delete, other than using private delete protocol.

Example 17.2 Debugging code using GsEventLog

topaz 1> run
[GsEventLog logDebug: 'About to perform divide by zero'.
 1 / 0.
 GsEventLog logDebug: 'After divide by zero'.
	on: Error 
	do: [:ex | GsEventLog logObject: ex.  ex resume].
topaz 1> run
GsEventLog current report
'2017-07-13 16:36:44.3820  24198 Trace  About to perform divide by zero
2017-07-13 16:36:44.3821  24198 Trace  a ZeroDivide occurred (error
  2026), reason:numErrIntDivisionByZero, attempt to divide 1 by zero
  a ZeroDivide occurred (error 2026), reason:numErrIntDivisionByZero,
  attempt to divide 1 by zero
2017-07-13 16:36:44.3822  24198 Trace  After divide by zero

Previous chapter

Next chapter