Class JDBCStorage
- java.lang.Object
-
- org.opends.server.backends.jdbc.JDBCStorage
-
- All Implemented Interfaces:
Closeable,AutoCloseable,ConfigurationChangeListener<JDBCBackendCfg>,Storage
public class JDBCStorage extends Object implements Storage, ConfigurationChangeListener<JDBCBackendCfg>
-
-
Constructor Summary
Constructors Constructor Description JDBCStorage(JDBCBackendCfg cfg, ServerContext serverContext)
-
Method Summary
All Methods Instance Methods Concrete Methods Modifier and Type Method Description ConfigChangeResultapplyConfigurationChange(JDBCBackendCfg cfg)Applies the configuration changes to this change listener.voidclose()voidcreateBackup(BackupConfig backupConfig)Creates a backup for this storage.StorageStatusgetStorageStatus()Returns the current status of the storage.booleanisConfigurationChangeAcceptable(JDBCBackendCfg configuration, List<org.forgerock.i18n.LocalizableMessage> unacceptableReasons)Indicates whether the proposed change to the configuration is acceptable to this change listener.Set<TreeName>listTrees()Lists the trees that exist in this storage.voidopen(AccessMode accessMode)Opens the storage engine to allow executing operations on it.<T> Tread(ReadOperation<T> readOperation)Executes a read operation.voidremoveBackup(BackupDirectory backupDirectory, String backupID)Removes a backup for this storage.voidremoveStorageFiles()Remove all files for a backend of this storage.voidrestoreBackup(RestoreConfig restoreConfig)Restores a backup for this storage.ImporterstartImport()Starts the import operation.booleansupportsBackupAndRestore()Returnstrueif this storage supports backup and restore.voidwrite(WriteOperation writeOperation)Executes a write operation.
-
-
-
Constructor Detail
-
JDBCStorage
public JDBCStorage(JDBCBackendCfg cfg, ServerContext serverContext)
-
-
Method Detail
-
isConfigurationChangeAcceptable
public boolean isConfigurationChangeAcceptable(JDBCBackendCfg configuration, List<org.forgerock.i18n.LocalizableMessage> unacceptableReasons)
Description copied from interface:ConfigurationChangeListenerIndicates whether the proposed change to the configuration is acceptable to this change listener.- Specified by:
isConfigurationChangeAcceptablein interfaceConfigurationChangeListener<JDBCBackendCfg>- Parameters:
configuration- The new configuration containing the changes.unacceptableReasons- A list that can be used to hold messages about why the provided configuration is not acceptable.- Returns:
- Returns
trueif the proposed change is acceptable, orfalseif it is not.
-
applyConfigurationChange
public ConfigChangeResult applyConfigurationChange(JDBCBackendCfg cfg)
Description copied from interface:ConfigurationChangeListenerApplies the configuration changes to this change listener.- Specified by:
applyConfigurationChangein interfaceConfigurationChangeListener<JDBCBackendCfg>- Parameters:
cfg- The new configuration containing the changes.- Returns:
- Returns information about the result of changing the configuration.
-
open
public void open(AccessMode accessMode) throws Exception
Description copied from interface:StorageOpens the storage engine to allow executing operations on it.- Specified by:
openin interfaceStorage- Parameters:
accessMode- Specify the access mode to this storage.- Throws:
NullPointerException- if accessMode is null.Exception- if a problem occurs with the underlying storage engine- See Also:
to release all resources once import is finished
-
getStorageStatus
public StorageStatus getStorageStatus()
Description copied from interface:StorageReturns the current status of the storage.- Specified by:
getStorageStatusin interfaceStorage- Returns:
- the current status of the storage
-
close
public void close()
-
removeStorageFiles
public void removeStorageFiles() throws StorageRuntimeExceptionDescription copied from interface:StorageRemove all files for a backend of this storage.- Specified by:
removeStorageFilesin interfaceStorage- Throws:
StorageRuntimeException- if removal fails
-
read
public <T> T read(ReadOperation<T> readOperation) throws Exception
Executes a read operation. In case of a read operation rollback, implementations must propagate the failure to the caller rather than replay the operation: unlikeWriteOperation, aReadOperationis not required to be idempotent, and several of them are not - they write to a stream, print, or accumulate state that a second attempt would double.A rolled back read is not replayed, as
Storage.read(ReadOperation)requires: two of the read operations of this server are not idempotent, and replaying them corrupts their result rather than repairing it.ExportJobruns the whole export inside a single read and its LDIF writer is opened once, so a replay appends the entries already written instead of truncating the file;VerifyJobaccumulates its counters in instance fields that no attempt resets, so a replay reports twice the entry count of the backend. Both are reachable while the server is online, since an export holds no more than a shared backend lock. A conflict therefore fails the read here, exactly as it did before the retry ofwrite(org.opends.server.backends.pluggable.spi.WriteOperation)was added.A connection the database dropped is not replayed either, for the same reason - but it is reported to the pool, which cannot notice one on its own: a borrow inside the alive window of
CachedConnection.ALIVE_BYPASS_PROPERTYasks the database nothing, so the statement that broke is the only place the drop is ever seen.
-
write
public void write(WriteOperation writeOperation) throws Exception
Executes a write operation. In case of a write operation rollback, implementations may replay the write operation rather than propagate the failure: aWriteOperationis required to be idempotent for exactly that reason. A replay may be bounded - by a number of attempts, by a window of time, or by both - so that a conflict which does not clear reaches the caller, or may go on for as long as the conflict lasts, the way a writer of a lock based engine waits for a lock; an engine which resolves every conflict by a rollback should bound it by time only, since a healthy write under concurrent load loses several in a row. The pluggable backend holds locks across this method, up to the exclusive lock of an entry container, and every thread waiting on one of those locks waits for as long as this method does.A caller that mutates state around this method must handle that bound being spent. Removing an entry from an in-memory map before the write so that a replay still finds the work to do, or reading configuration back out of the operation once it returns, both assume the write is applied; when it is not, this method throws with that state already changed and the transaction not applied, and the caller is the only place that can reconcile the two.
Storage.write(WriteOperation)requires an implementation to retry a rolled back operation until it succeeds, andWriteOperationis documented as idempotent for exactly that reason;PDBStorage.write(WriteOperation)already does so on the conflict exception of its own engine. The loop is bounded here, unlike PDBStorage: the database may be shared with writers outside this server, so a conflict is not guaranteed to clear and failing the operation is better than never returning. It is bounded twice - byMAX_RETRIESattempts and by theRETRY_WINDOW_NANOSwall-clock window - because an attempt is not guaranteed to be short: a conflict an engine reports only after its own lock wait timeout would otherwise multiply that wait by the attempt count. The window is checked between attempts, so an attempt already running is never interrupted: a conflicted operation holds its caller for the window plus one attempt.What makes those two bounds enough is that the attempt itself is bounded: the transaction runs under
ROW_LOCK_TIMEOUT_PROPERTY, armed on the session here and taken off again before the connection is released, so the wait ahead of a conflict ends inside the window rather than outlasting it (#915). Where nothing bounds the attempt - oracle, which has no such setting, and a deployment that turns the property off - the window is shorter than the wait that precedes a conflict the engine reports promptly, so measured against such a conflict it does not bound that wait but only leaves the operation with no replay at all, which is what master did with the deadlock of issue #903. One replay is therefore granted to a prompt conflict whatever the clock says; seegrantedPastTheWindow(int, long, org.opends.server.backends.jdbc.JDBCStorage.Conflict). Such a conflict holds its caller for two attempts when that is longer than the window plus one.Only the operation itself is replayed: a failure of
getConnection()or of the implicitConnection.close()- which returns the connection to the pool after a rollback - leaves the loop, so that a completed write is never replayed because releasing its connection failed.A connection the database dropped is replayed as well, on a connection the next attempt borrows of its own. That is what makes the alive window of
CachedConnection.ALIVE_BYPASS_PROPERTYsafe to leave on: a connection handed out unvalidated and found dead costs an attempt rather than the operation, and a write of the replication replay - which records a failed operation as applied and advances the server state past it, see #889 - never sees it. Only while nothing of the attempt may have been committed yet, though: seereplayReason(org.opends.server.backends.jdbc.JDBCStorage.Conflict, java.lang.Throwable, boolean, boolean, boolean, org.opends.server.backends.jdbc.JDBCStorage.Dialect, org.opends.server.backends.jdbc.JDBCStorage.ArmedLockBound).
-
listTrees
public Set<TreeName> listTrees()
Lists the trees that exist in this storage.Answered from the catalog of the backend rather than from the trees this process happens to have touched:
removeStorageFiles()runs before anything has touched one (#888).What a tool has to be shown is not what a clear may drop: the shared compressed schema trees are deliberately not enrolled - a backend must not offer a tree another one may own for removal - and would go unnamed by
dbtestfor it, so they are added here when their tables are there.catalogTables(Connection, TableScope)is what the removal reads, and it names them not.The catalog itself is among the names, being a tree of this backend like any other:
dbtest list-raw-dbscounts it anddump-raw-dbresolves its name, which is the one way of seeing from outside the server what a clear of this backend would drop.
-
startImport
public Importer startImport() throws ConfigException, StorageRuntimeException
Description copied from interface:StorageStarts the import operation.- Specified by:
startImportin interfaceStorage- Returns:
- a new Importer object which must be closed to release all resources
- Throws:
ConfigException- if there is a problem with the configurationStorageRuntimeException- if a problem occurs with the underlying storage engine- See Also:
to release all resources once import is finished
-
supportsBackupAndRestore
public boolean supportsBackupAndRestore()
Description copied from interface:StorageReturnstrueif this storage supports backup and restore.- Specified by:
supportsBackupAndRestorein interfaceStorage- Returns:
trueif this storage supports backup and restore.
-
createBackup
public void createBackup(BackupConfig backupConfig) throws DirectoryException
Description copied from interface:StorageCreates a backup for this storage.- Specified by:
createBackupin interfaceStorage- Parameters:
backupConfig- The configuration to use when performing the backup.- Throws:
DirectoryException- If a Directory Server error occurs.
-
removeBackup
public void removeBackup(BackupDirectory backupDirectory, String backupID) throws DirectoryException
Description copied from interface:StorageRemoves a backup for this storage.- Specified by:
removeBackupin interfaceStorage- Parameters:
backupDirectory- The backup directory structure with which the specified backup is associated.backupID- The backup ID for the backup to be removed.- Throws:
DirectoryException- If it is not possible to remove the specified backup.
-
restoreBackup
public void restoreBackup(RestoreConfig restoreConfig) throws DirectoryException
Description copied from interface:StorageRestores a backup for this storage.- Specified by:
restoreBackupin interfaceStorage- Parameters:
restoreConfig- The configuration to use when performing the restore.- Throws:
DirectoryException- If a Directory Server error occurs.
-
-