Class JEStorage

    • Constructor Detail

      • JEStorage

        public JEStorage​(JEBackendCfg cfg,
                         ServerContext serverContext)
                  throws ConfigException
        Creates a new JE storage with the provided configuration.
        Parameters:
        cfg - The configuration.
        serverContext - This server instance context
        Throws:
        ConfigException - if memory cannot be reserved
    • Method Detail

      • read

        public <T> T read​(ReadOperation<T> operation)
                   throws Exception
        Description copied from interface: Storage
        Executes a read operation. In case of a read operation rollback, implementations must propagate the failure to the caller rather than replay the operation: unlike WriteOperation, a ReadOperation is 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.
        Specified by:
        read in interface Storage
        Type Parameters:
        T - type of the value returned
        Parameters:
        operation - the read operation to execute
        Returns:
        the value read by the read operation
        Throws:
        Exception - if a problem occurs with the underlying storage engine
      • write

        public void write​(WriteOperation operation)
                   throws Exception
        Executes a write operation. In case of a write operation rollback, implementations may replay the write operation rather than propagate the failure: a WriteOperation is 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.

        A transaction JE ends with a LockConflictException is replayed, bounded twice: by MAX_RETRIES attempts and by the MAX_RETRY_WINDOW_NANOS wall-clock window, whichever is spent first - except that the window alone never ends the replays before one has been made. It is bounded for the reason PDBStorage bounds its loop: the configuration change paths of the pluggable backend hold an entry container's exclusive lock across this method, and every reader of that suffix then waits - untimed and uninterruptibly - until it returns, so a conflict that never clears would park every worker thread of that suffix rather than fail one operation.

        JE raises the conflict from inside the operation - a record read or write, never the commit, which takes no lock - and the transaction wraps it in a StorageRuntimeException, which is unwrapped below before it is matched. The operation may catch it there; the transaction is then abort-only and commit() raises the conflict again, bare, so an attempt which swallowed its conflict commits nothing and is replayed all the same. With the shipped configuration the only conflict is the deadlock: je.lock.timeout is 0, so a writer waits for a lock rather than times out, and JE ends one transaction of a cycle - chosen at random - as soon as a wait would close it. A lock timeout set through ds-cfg-je-property makes a wait that long a conflict as well.

        The replay backs off first, though JE would make it wait for the locks it lost anyway: JE locks a record by the LSN of its current version, and the abort of the victim, which hands the waiting survivor the lock it asked for, undoes the version that lock belongs to - the survivor then has to lock the version the undo put back, and a replay which comes back at once wins that race, holds the record when the survivor asks again, and the same deadlock forms with the roles drawn afresh. JE's own retry example sleeps before retrying for this reason; without the sleep the deadlock of two writers was seen to form three times in a row.

        An interrupt reaches the loop only in that sleep, and the loop does not restore the flag the sleep cleared: JE invalidates the whole environment when a thread carrying the interrupt flag touches it - the transaction registry's latch is acquired interruptibly - and the caller's own failure road makes such a call (EntryContainer.writeTrustState). The interrupt is reported instead, with the conflict being replayed, as the suppressed exceptions of the StorageRuntimeException thrown.

        Once the bound is spent the conflict is reported as a StorageRuntimeException naming the backend, the attempts spent, the time they took and which of the two bounds ran out. It carries the conflict as a suppressed exception rather than as its cause: a cause is unwrapped below and thrown in its place, and EntryContainer.throwAllowedExceptionTypes likewise passes a StorageRuntimeException through untouched only while it has no cause. Given a cause, both hand the caller the bare conflict instead, whose message is JE's account of its lock table - all an LDAP client used to be told after the single attempt.

        Specified by:
        write in interface Storage
        Parameters:
        operation - the write operation to execute
        Throws:
        Exception - if a problem occurs with the underlying storage engine, including a conflict that outlasted the replays the implementation makes
      • supportsBackupAndRestore

        public boolean supportsBackupAndRestore()
        Description copied from interface: Storage
        Returns true if this storage supports backup and restore.
        Specified by:
        supportsBackupAndRestore in interface Storage
        Returns:
        true if this storage supports backup and restore.
      • getDirectory

        public File getDirectory()
        Description copied from interface: Backupable
        Returns the directory which acts as the root of all files to backup and restore.
        Specified by:
        getDirectory in interface Backupable
        Returns:
        the root directory
      • beforeRestore

        public Path beforeRestore()
                           throws DirectoryException
        Description copied from interface: Backupable
        Called before the restore operation begins.

        In case of direct restore, the backupable entity should take any action to save a copy of existing data before restore operation. Saving includes removing the existing data and copying it in a save directory.

        Specified by:
        beforeRestore in interface Backupable
        Returns:
        the directory where current files are saved. It may be null if not applicable.
        Throws:
        DirectoryException - If an error occurs.
      • isDirectRestore

        public boolean isDirectRestore()
        Description copied from interface: Backupable
        Indicates if restore is done directly in the restore directory.
        Specified by:
        isDirectRestore in interface Backupable
        Returns:
        true if restore is done directly in the restore directory provided by getDirectory() method, or false if restore is done in a temporary directory.
      • afterRestore

        public void afterRestore​(Path restoreDirectory,
                                 Path saveDirectory)
                          throws DirectoryException
        Description copied from interface: Backupable
        Called after the restore operation has finished successfully.

        For direct restore, the backupable entity can safely discard the saved copy. For indirect restore, the backupable entity should switch the restored directory to the final restore directory.

        Specified by:
        afterRestore in interface Backupable
        Parameters:
        restoreDirectory - The directory in which files have actually been restored. It is never null.
        saveDirectory - The directory in which current files have been saved. It may be null if beforeRestore() returned null.
        Throws:
        DirectoryException - If an error occurs.
      • createBackup

        public void createBackup​(BackupConfig backupConfig)
                          throws DirectoryException
        Description copied from interface: Storage
        Creates a backup for this storage.
        Specified by:
        createBackup in interface Storage
        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: Storage
        Removes a backup for this storage.
        Specified by:
        removeBackup in interface Storage
        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: Storage
        Restores a backup for this storage.
        Specified by:
        restoreBackup in interface Storage
        Parameters:
        restoreConfig - The configuration to use when performing the restore.
        Throws:
        DirectoryException - If a Directory Server error occurs.
      • listTrees

        public Set<TreeName> listTrees()
        Description copied from interface: Storage
        Lists the trees that exist in this storage.
        Specified by:
        listTrees in interface Storage
        Returns:
        a set of TreeNames representing the trees that exist in this storage
      • isConfigurationChangeAcceptable

        public boolean isConfigurationChangeAcceptable​(JEBackendCfg newCfg,
                                                       List<org.forgerock.i18n.LocalizableMessage> unacceptableReasons)
        Description copied from interface: ConfigurationChangeListener
        Indicates whether the proposed change to the configuration is acceptable to this change listener.
        Specified by:
        isConfigurationChangeAcceptable in interface ConfigurationChangeListener<JEBackendCfg>
        Parameters:
        newCfg - 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 true if the proposed change is acceptable, or false if it is not.
      • getStorageStatus

        public StorageStatus getStorageStatus()
        Description copied from interface: Storage
        Returns the current status of the storage.
        Specified by:
        getStorageStatus in interface Storage
        Returns:
        the current status of the storage
      • diskFullThresholdReached

        public void diskFullThresholdReached​(File directory,
                                             long thresholdInBytes)
        Description copied from interface: DiskSpaceMonitorHandler
        Notifies that the registered "full" threshold have been reached.
        Specified by:
        diskFullThresholdReached in interface DiskSpaceMonitorHandler
        Parameters:
        directory - the directory for which the threshold has been triggered
        thresholdInBytes - the threshold value in bytes
      • diskLowThresholdReached

        public void diskLowThresholdReached​(File directory,
                                            long thresholdInBytes)
        Description copied from interface: DiskSpaceMonitorHandler
        Notifies that the registered "low" threshold have been reached.
        Specified by:
        diskLowThresholdReached in interface DiskSpaceMonitorHandler
        Parameters:
        directory - the directory for which the threshold has been triggered
        thresholdInBytes - the threshold value in bytes
      • diskSpaceRestored

        public void diskSpaceRestored​(File directory,
                                      long lowThresholdInBytes,
                                      long fullThresholdInBytes)
        Description copied from interface: DiskSpaceMonitorHandler
        Notifies that the free disk space is now above both "low" and "full" thresholds.
        Specified by:
        diskSpaceRestored in interface DiskSpaceMonitorHandler
        Parameters:
        directory - the directory for which the threshold has been triggeredTODO
        lowThresholdInBytes - the low threshold value in bytes
        fullThresholdInBytes - the full threshold value in bytes