The repository is the logical unit that represents the universe of shared objects that are stored within a GemStone/S 64 Bit system. The logical repository maps to one or more physical extent files in the file system, or to data on one or more raw disk partitions.
This chapter explains how the repository grows, and tells you how to perform a number of administrative tasks related to the repository and extents.
The Repository and Extents
describes the relationship between the repository and the extents, and why the repository grows.
Adding and Removing Extents
provides instructions on adding and removing extents from the repository.
Reallocating Existing Objects Among Extents
describes how to change the way objects are distributed over the extents.
Shrinking the Repository
provides instructions for reducing the size of the repository extents.
Checking Page Fragmentation
describes page fragmentation and how to address it.
Disk Space and Commit Record Backlogs
avoiding running out of space due to an idle session creating a commit record backlog.
Recovering from Disk-Full Conditions
describes the actions taken by the Stone to prevent running out of space, what to do to avoid this condition, and how to recover.
Within GemStone Smalltalk, the repository – the logical unit holding the universe of shared GemStone objects – is the single instance of Class Repository, with the name SystemRepository. The logical repository is represented on disk by one or more physical extent files in the file system raw disk partitions. This is described under Extents, Tranlogs, and disk space.
Whenever GemStone performs a checkpoint, it makes sure that transactions committed before the checkpoint have been written to the repository extents. The maximum time between checkpoints is set by the STN_CHECKPOINT_INTERVAL configuration option; the default is five minutes, but various factors may cause a checkpoint to occur sooner. The checkpoint limits the amount of time that is needed to recover from a system crash by guaranteeing that the data for the transaction is written to the extent and not just to the transaction log. For information, see Controlling Checkpoint Frequency.
Repository extents not only have to hold the data in your database, they also need to hold the changes all the users make, and coordinate the views of each user so the user has a consistent view of the data. All these activities require space in the extents.
The repository begins in the compact form of $GEMSTONE/data/extent0.dbf. As repository activity progresses, the extent file expands for a variety of reasons, in increments of 16 MB. Not only does your new application data require space, but space is required for internal structures that organize and manage the objects. Sessions, and their transactional views of the repository, also require space.
Garbage objects—objects that are no longer referenced, or the older versions of objects— also use space in the repository until they are garbage collected. To manage the size of the extents, you need to regularly perform garbage collection on your GemStone repository. The frequency can vary from monthly to daily, depending on the amount of activity in the repository. Without garbage collection, the repository will continue to grow and be filled with wasted space. See Chapter 14, “Managing Growth”, for a discussion of garbage collection in GemStone.
Use the methods Repository>>fileSize and Repository>>freeSpace to obtain reports about the logical repository as a whole.
The result of the message fileSize is the total size of the repository in bytes, including all extent files. If the repository consists of a single extent file, it is ordinarily the same result as you would obtain by using the operating system commands to find file size. For example:
topaz 1> printit
SystemRepository fileSize
%
153092096
The result of freeSpace tells how much space (in bytes) is available for allocation within the repository at its current size. Free space is equal to the sum (for all extents in the repository) of the number of free pages in each extent multiplied by the page size. This space does not include fragments on partially filled data pages.
topaz 1> printit
SystemRepository freeSpace
%
26411008
Depending on the configuration options selected and the available disk space, the Stone repository monitor may be able to create additional free space by enlarging the repository.
If your configuration has more than one extent, use Repository>>fileSizeReport to generate statistics about each individual extent and also totals for the entire repository. (The heading “Extent #1” identifies the primary extent regardless of its file name, which initially is extent0.dbf.)
topaz 1> printit
SystemRepository fileSizeReport
%
Extent #1
Filename = /users/extents/extent0.dbf
File size = 208.00 Megabytes
Space available = 3.69 Megabytes
-----------
Extent #2
Filename = /users/extents/extent1.dbf
File size = 74.00 Megabytes
Space available = 4.77 Megabytes
------
Totals
Repository size = 282.00 Megabytes
Free Space = 8.45 Megabytes
GemStone provides two ways to add extents:
To prevent the repository from becoming full, you can dynamically add another extent to the Stone configuration. This section describes the Smalltalk methods that allow you to do this.
For general information about multiple extents, see Configuring the Repository Extents.
When a new extent is dynamically added to the logical repository through Smalltalk, sessions that are currently logged in must have access to the new extent. The possibility exists that an online session may terminate because it cannot open a new extent. Reasons for this condition could range from the inability to start a remote page server process to file permission problems.
CAUTION
The operating system creates the new extents with the ownership and permissions of the Stone repository monitor process. If these permissions are not the same as for other extents, you should use operating system commands to modify them as soon as possible. Such changes can be made without stopping the Stone.
A session’s view of which files make up the logical repository is updated whenever one of the following events occurs:
An explicit commit or abort may succeed but then cause the session to be terminated because of the inability to mount new extents immediately after the commit or abort operation.
Privileges required: FileControl.
The Smalltalk method createExtent:extentFilename creates a new repository extent with the given extent file name (specified as a String). The new extent has no maximum size. The extent must be located on the machine running the Stone process; NFS-mounted disks are only allowed if your stone is configured to allow NFS mounted disks; see STN_ALLOW_NFS_EXTENTS. For example:
topaz 1> printit
SystemRepository createExtent: '$GEMSTONE/data/extent2.dbf'
%
You can execute this method when other users are logged in.
The Stone creates the new extent file, and it also appends the augmented extent list to your configuration file:
# DBF_EXTENT_NAMES written by Stone (user Bob) on 8/12/17 12:56:18 PDT
DBF_EXTENT_NAMES = "$GEMSTONE/data/extent0.dbf",
"$GEMSTONE/data/extent1.dbf",
"!TCP@mozart#dbf!/users/gemstone/data/extent2.dbf;"
If the given file already exists, the method returns an error and the specified extent is not added to the repository.
Creating an extent with this method bypasses any setting you may have specified for the DBF_PRE_GROW configuration option at system startup. Because extents created with this method have no maximum size and do not have an entry in a list setting for DBF_PRE_GROW, they cannot be pre-grown. If the repository is using weighted allocation, the new extent will be given a weight equal to the average weight of all other extents. (weighted allocation is discussed here.)
If this method is run from a session on a host remote from the Stone, extentFilename must include a Network Resource String (NRS) specifying the Stone host. The syntax is shown above in the excerpt from the augmented configuration file. For information about NRS syntax, see Appendix C.
Privileges required: FileControl.
The Smalltalk method createExtent:extentFilename withMaxSize:aSmallInteger creates a new repository extent with the specified extentFilename and sets the maximum size of that extent to the specified size. You can execute this method when other users are logged in.
The size must be a non-zero positive integer representing the maximum physical size of the file in MB.
If the specified extent file already exists, this method returns an error and the extent is not added to the logical repository.
If DBF_PRE_GROW is set to True, this method will cause the newly created extent to be pre-grown to the given size. If the pre-grow operation fails, then this method will return an error and the new extent will not be added to the logical repository.
The only way to remove an extent file is by first performing a Smalltalk full backup and restore to move the contents of that extent to other extents.
Privileges required: FileControl.
Reducing the number of existing extents requires special steps to ensure data integrity. If you must remove an extent file, follow this procedure:
Step 1. Back up your repository using the Smalltalk full backup procedure described under How To Make a Smalltalk Full Backup.
You cannot use an online or offline extent backup to remove or shrink extents (obviously).
Step 2. Shut down the Stone repository monitor.
Step 3. Modify the DBF_EXTENT_NAMES configuration option to show the new extent structure.
Step 4. Restore the repository from your Smalltalk full backup. Follow the GemStone restore procedure described under Restoring from a Full Backup.
If you want to reallocate existing objects among two or more extents, the procedure depends in part on whether you are also changing the number of extents. Because changes to the DBF_ALLOCATION_MODE configuration option directly affect only the subsequent allocation of pages for new or modified objects, additional steps are necessary.
If you are increasing or decreasing the number of extents and want to change allocation of existing objects as part of that operation, perform a Smalltalk full backup, then restore the backup after setting appropriate weights for DBF_ALLOCATION_MODE.
For example, suppose your existing repository contains 800 MB and you want to divide them about equally between the existing extent and a new one. To populate each extent with about 400 MB, follow this procedure:
Step 1. Back up your repository, using the Smalltalk full backup procedure described under How To Make a Smalltalk Full Backup. You cannot use an online or offline extent backup to redistribute objects (obviously).
Step 2. Shut down the Stone repository monitor.
Step 3. Modify the DBF_EXTENT_NAMES configuration parameter to show the new extent structure.
DBF_EXTENT_NAMES = $GEMSTONE/data/extent0.dbf,$GEMSTONE/data/extent1.dbf;
Step 4. Edit DBF_ALLOCATION_MODE to reflect the intended distribution of pages (see Allocating Data to Multiple Extents). For example:
DBF_ALLOCATION_MODE = 10, 10;
Step 5. Restore the repository from your Smalltalk full backup, using the procedure described under Restoring from a Full Backup.
If objects in the repository were explicitly clustered using instances of ClusterBucket that explicitly specified the first extent, those objects may tend to migrate back to that extent over time. You can prevent such migration by placing size limits on the existing extent, or by explicitly reclustering those objects in the new extent using a ClusterBucket that specifies either an extentId of nil or the extentId of the new extent. For information about clustering, refer to the Programming Guide.
Changes to DBF_ALLOCATION_MODE directly affect only the subsequent allocation of pages for new or modified objects. When you restore into a repository with the same number of extents, the distribution of the original repository will be used in the restored repository, regardless of the DBF_ALLOCATION_MODE.
To change the allocation of existing objects, you can either restore into a repository with a different number of extents (as discussed in the previous section) or you can specify a temporary maximum size on the extent files during the restore, to force objects to be distributed as you want them.
For example, suppose your existing repository has three extents, and that you are running in sequential allocation mode. The first extent has 600 MB, while the second and third extents are 4 MB each (the minimum size). You now want to redistribute the objects so they are spread evenly over all three extents. You cannot simply change the DBF_ALLOCATION_MODE, and restore a backup into three extents; existing objects would be distributed according to the original allocation mode, that is, entirely in the first extent. Only new objects created after the restore would be created evenly over the three extents.
To populate the three extents evenly, you can follow this procedure:
Step 1. Back up your repository, using the Smalltalk full backup procedure described under How To Make a Smalltalk Full Backup.
Step 2. Shut down the Stone repository monitor.
Step 3. Edit the DBF_EXTENT_SIZES configuration option to limit the size of the first extent temporarily to 200 MB. For example:
DBF_EXTENT_SIZES = 200MB, , ;
Step 4. Edit the DBF_ALLOCATION_MODE configuration option to reflect the intended distribution of pages (see Allocating Data to Multiple Extents). This setting determines the distribution of new or modified objects. For example:
DBF_ALLOCATION_MODE = 10, 10, 10;
Step 5. Restore the repository from your Smalltalk full backup, using the procedure described under Restoring from a Full Backup.
Step 6. If you want the first extent to grow beyond the temporary limit you set in Step 3, stop the Stone after you restore the repository. Edit the configuration file again, either specifying a higher limit or no limit. For example:
DBF_EXTENT_SIZES = , , ;
If objects in the repository were explicitly clustered using instances of ClusterBucket that explicitly specified the first extent, those objects may tend to migrate back to that extent over time. You can prevent such migration by maintaining the size limit set in Step 3, or by explicitly reclustering those objects in the new extent using a ClusterBucket that specifies either an extentId of nil or the extentId of the new extent. For information, refer to the Programming Guide.
Privileges required: SystemControl, GarbageCollection, and FileControl.
To shrink the repository to its minimum size, make a Smalltalk full backup. Then take the repository offline and restore the backup into a copy of the GemStone distribution repository. Use the following procedure, which compacts the repository into the minimum set of consecutive pages.
Step 1. Mark your repository for garbage collection, and wait for GemStone to complete the garbage collection and reclaim the space.
topaz 1> printit
SystemRepository markForCollection
%
The time required depends on several factors: the size of the repository, the number of Reclaim Gem sessions currently running, and (in multi-user mode) the status of other sessions.
If other users are logged in, space will not be reclaimed until all sessions have committed or aborted any transactions concurrent with the markForCollection process.
For further information on markForCollection and the garbage collection process, see Chapter 14, “Managing Growth”. Details on the markForCollection method are under MarkForCollection.
Step 2. Make a Smalltalk full backup of your repository by sending it the message fullBackupTo: or fullBackupTo: MBytes:.
topaz 1> printit
SystemRepository fullBackupTo: '/users/backups/August_20'.
%
This example writes the backup to a single disk file. If you need to write multiple files, see The fullBackupTo: Methods.
Step 3. Take the repository offline:
topaz 1> printit
System shutDown
%
Step 4. Remove the existing repository extents. Obtain a copy of the distribution repository as the first (primary) extent by using the copydbf command. For example, assuming that all of your GemStone extents are in $GEMSTONE/data:
% cd $GEMSTONE/data
% rm extentNames
% copydbf $GEMSTONE/bin/extent0.dbf primaryExtentName
Use chmod to set the extent permission to what you ordinarily use for your repository.
Step 5. To put the repository back online, issue the startstone command:
% startstone -R gemStoneName
If you do not specify gemStoneName, startstone defaults to gs64stone.
Step 6. Log in to linked Topaz again.
NOTE
To perform the remaining parts of this procedure, you must be the only user logged in to GemStone. Logins will be disabled when you start the next step.
Step 7. Restore the repository by using the method Repository>>restoreFromBackup:fileOrDevice, using the same file or device as in Step 2. Because it is being restored into a copy of the initial repository, the restored repository will be compressed to the minimum space. This example restores the backup from a single disk file:
topaz 1> printit
SystemRepository restoreFromBackup: '/users/backups/August_20'
%
If you need to restore multiple files, use the method Repository>>restoreFromBackups:fileOrDeviceArray instead:
topaz 1> printit
SystemRepository restoreFromBackups:
#( '/users/backups/August_20.1'
'/users/backups/August_20.2' )
%
(For more information, see Restoring from a Full Backup.)
GemStone reads the backup(s) and rebuilds the repository in a “shadow” object space that is invisible to users at this time. If the restore succeeds, GemStone commits the restore and returns a summary in the form of a nonfatal error message like the following:
Restore from full backup completed with 616227 objects restored.
Each restore operation, on completion, terminates its GemStone session. You will need to log in again before performing the next restore operation.
Step 8. Between the time the full backup was started (Step 2) and the time the repository was shut down (Step 3), there may have been transactions on your repository. To ensure that no work is lost, restore from transaction logs and commit the restore. For example:
topaz 1> printit
SystemRepository restoreFromCurrentLogs
%
Restore from transaction log(s) succeeded.
topaz> login
<details omitted>
successful login
topaz 1> printit
SystemRepository commitRestore
%
Restore from transaction log(s) succeeded. commitRestore succeeded
Space within the repository is managed in pages having a fixed size of 16 KB. It is possible for these pages to become fragmented—that is, only partially filled with objects. GemStone automatically schedules reclaim of pages with greater than 10% free space as part of its garbage collection activity.
You can inquire about the amount of fragmentation in the repository by executing the following expression.
SystemRepository pagesWithPercentFree:aPercentage
Typical values of aPercentage range from 10 to 25. This method returns an array containing the following statistics:
pagesWithPercentFree: executes using the multi-threaded scan. See Multi-Threaded Scan for details.
Sessions only update their view of the repository when they commit or abort. The repository must keep a copy of each session’s view so long as the session is using it, even if other sessions frequently commit changes and create new views (commit records). Storing the original view and all the intermediate views uses up space in the repository, and can result in the repository running out of space. To avoid this problem, all sessions in a busy system should commit or abort regularly.
For a session that is not in a transaction, if the number of commit records exceeds the value of STN_CR_BACKLOG_THRESHOLD, the Stone repository monitor signals the session to abort by signaling TransactionBacklog (also called “sigAbort”). If the session does not abort, the Stone repository monitor terminates it.
Sessions that are in transaction are not subject to losing their view forcibly. Sessions in transaction enable receipt of the signal TransactionBacklog, and handle it appropriately, but it is optional. It is important that sessions do not stay in transaction for long periods in busy systems; this can result in the Stone running out of space and shutting down. However, sessions that run in automatic transaction mode are always in transaction; as soon as they commit or abort, they begin a new transaction. (For a discussion of automatic and manual transaction modes, see the “Transactions and Concurrency Control” chapter of the Programming Guide.)
To avoid running out of disk space, we recommend that you use manual transaction mode whenever possible. To enter manual transaction mode:
topaz> printit
System transactionMode: #manualBegin
%
At the point that this session needs to commit a change, begin a transaction manually, then make the changes:
topaz> printit
System beginTransaction .
AllUsers addNewUserWithId: #Jane password: 'gemstone' .
System commitTransaction
%
After you commit (or abort) the transaction, your session will return to waiting outside of a transaction.
Even in manual transaction mode, it is possible to cause a commit record backlog, depending on how your system is configured. Sessions should ensure that they commit or abort regularly, or set up sigAbort handlers to abort when requested by the Stone. A sigAbort handler may be as simple as this:
Exception
installStaticException:
[ :exception :GSdictionary :errID :array |
System abortTransaction.
System enableSignaledAbortError).
Note that a session that is entirely idle does not become aware of the signal to abort, and may timeout and be terminated by the stone in spite of the handler. If your application may have idle sessions, we recommend setting up a timer that causes regular aborts when the session is otherwise idle.
Sessions that are in transaction, and therefore immune from the sigAbort mechanism, may also be signaled when there is a commit record backlog. When the number of commit records exceeds the value of STN_CR_BACKLOG_THRESHOLD, and the session holding the oldest commit record is in transaction, the Stone repository monitor signals the session by sending TransactionBacklog. The session then has the opportunity to perform a continueTransaction to update its view of unmodified objects. It may also commit or abort. Unlike sigAbort, the session can choose to ignore this message and will not receive further signals from the stone.
The Stone repository monitor has two critical needs for disk space. It must be able to:
Whenever the Stone cannot log transactions or cannot find sufficient free space in the repository, it issues an error message to any session logged in as DataCurator or SystemUser. If users report that GemStone appears to be hung or that they get a disk-full error while logging in, you should check one of these administrative logins for such a message. The message is also written to the Stone’s log file.
The following topics explain the Stone’s actions in greater detail and describe steps you can take to provide sufficient space.
For details on how tranlog full conditions are handled, see Recovering from Disk-Full Conditions.
The Stone takes a number of actions to prevent the repository from becoming completely full. If the free space remaining in the repository falls below the level set by the STN_FREE_SPACE_THRESHOLD configuration option and the Stone cannot allocate more space in any extent, it takes the following actions to prevent a system crash:
1. It becomes more aggressive about disposing of commit records so that garbage collection can proceed. (If the Stone is very busy, a backlog of commit records can accumulate.)
2. It starts a checkpoint if there isn't one in progress and reduces the checkpoint interval to three minutes until the condition clears. (This checkpoint may free pages that have been reclaimed.)
3. It writes a message to the Stone log to indicate the condition.
4. It prevents new logins except for DataCurator and SystemUser accounts. It issues a disk-full error to other sessions attempting to log in.
5. It signals the Exception RepositoryError to any sessions logged in (or logging in) as DataCurator or SystemUser so that they know disk space is becoming critical.
6. It signals Gem session processes to return all except five free pages per extent. It responds to requests for additional pages by allocating only five pages at a time.
7. If the free space available drops below 400 KB (50 pages), the Stone stops responding to page requests from sessions that are not logged in as DataCurator or SystemUser. This action prevents users from acquiring all of the available space, which would cause the system to crash. Gem session processes appear to “hang” while they are waiting for pages. The unhonored page requests are granted when the free space goes back above the threshold.
8. If the previous steps do not solve the problem within the time specified by the STN_DISKFULL_TERMINATION_INTERVAL configuration option, then the Stone begins to terminate sessions holding on to the oldest commit record even if the session is in a transaction. This action applies to any user session, including logins as SystemUser and DataCurator. Allowing the Stone to dispose of the commit record allows additional garbage collection.
NOTE
You can configure the Stone to never terminate sessions by setting STN_DISKFULL_TERMINATION_INTERVAL to 0; however, doing so increases the risk of GemStone shutting down because of a lack of free space in the repository.
9. When the condition clears, another message is written to the Stone log and operation returns to normal.
If you see a message like the following while logged in as DataCurator or SystemUser or in the Stone log, disk space is becoming critical:
The repository is currently running below the freeSpaceThreshold.
When the system must dynamically expand the repository, it checks one extent at a time, in the order dictated by the allocation strategy, to see if that extent can be expanded to create more space. When no extents can be extended and the free space is below STN_FREE_SPACE_THRESHOLD, the Stone takes the actions described above.
Failure to expand an extent has two possible causes: either the disk containing the extent is full, or the extent has reached its maximum size as set by the DBF_EXTENT_SIZES configuration option.
There are a number of things you can do to create more space in an existing extent, or you can create a new extent. Each of these actions may create sufficient additional space for immediate needs: