/**
   * Create a database environment to represent the data in envHome. dbHome. Properties from the
   * je.properties file in that directory are used to initialize the system wide property bag.
   * Properties passed to this method are used to influence the open itself.
   *
   * @param envHome absolute path of the database environment home directory
   * @param envConfig
   * @throws DatabaseException on all other failures
   */
  public EnvironmentImpl(File envHome, EnvironmentConfig envConfig) throws DatabaseException {

    try {
      this.envHome = envHome;
      envState = DbEnvState.INIT;

      /* Set up configuration parameters */
      configManager = new DbConfigManager(envConfig);
      configObservers = new ArrayList();
      addConfigObserver(this);

      /*
       * Decide on memory budgets based on environment config params and
       * memory available to this process.
       */
      memoryBudget = new LogBufferBudget(this, configManager);

      /*
       * Set up debug logging. Depending on configuration, add handlers,
       * set logging level.
       */
      // envLogger = initLogger(envHome);
      /*
       * Essential services. These must exist before recovery.
       */
      hook_readProperties(configManager);
      forcedYield = configManager.getBoolean(EnvironmentParams.ENV_FORCED_YIELD);
      isNoLocking = !(configManager.getBoolean(EnvironmentParams.ENV_INIT_LOCKING));
      isReadOnly = configManager.getBoolean(EnvironmentParams.ENV_RDONLY);

      fileManager = new FileManager(this, envHome, isReadOnly);
      if (!envConfig.getAllowCreate() && !fileManager.filesExist()) {
        throw new DatabaseException(
            "Enviroment creation isn't allowed, "
                + " but there is no pre-existing "
                + " environment in "
                + envHome);
      }

      logManager = new SyncedLogManager(this, isReadOnly);

      inMemoryINs = new INList(this);
      txnManager = new TxnManager(this);

      /*
       * Make sure that either log-size-based or time-based checkpointing is
       * enabled.
       */
      checkpointer = new Checkpointer(this);
      cleaner = new Cleaner(this, "Cleaner");
      /*
       * Daemons are always made here, but only started after recovery. We
       * want them to exist so we can call them programatically even if
       * the daemon thread is not started.
       */
      createDaemons();

      /*
       * Recovery will recreate the dbMapTree from the log if it exists.
       */
      dbMapTree = new DbTree(this);

      referenceCount = 0;

      /*
       * Do not do recovery and start daemons if this environment is for a
       * utility.
       */
      if (configManager.getBoolean(EnvironmentParams.ENV_RECOVERY)) {

        /*
         * Run recovery. Note that debug logging to the database log is
         * disabled until recovery is finished.
         */
        try {
          RecoveryManager recoveryManager = new RecoveryManager(this);
          lastRecoveryInfo = recoveryManager.recover(isReadOnly);
        } finally {
          try {
            /* Flush to get all exception tracing out to the log. */
            logManager.flush();
            fileManager.clear();
          } catch (IOException e) {
            throw new DatabaseException(e.getMessage());
          }
        }
      } else {
        isReadOnly = true;
        noComparators = true;
      }

      /* Start daemons after recovery. */
      runOrPauseDaemons(configManager);

      /*
       * Cache a few critical values. We keep our timeout in millis
       * instead of microseconds because Object.wait takes millis.
       */
      lockTimeout = PropUtil.microsToMillis(configManager.getLong(EnvironmentParams.LOCK_TIMEOUT));
      txnTimeout = PropUtil.microsToMillis(configManager.getLong(EnvironmentParams.TXN_TIMEOUT));

      /* Mark as open. */
      open();
    } catch (DatabaseException e) {

      /* Release any environment locks if there was a problem. */
      if (fileManager != null) {
        try {
          fileManager.close();
        } catch (IOException IOE) {

          /*
           * Klockwork - ok Eat it, we want to throw the original
           * exception.
           */
        }
      }
      throw e;
    }
  }
Example #2
0
  /**
   * Rollback the changes to this txn's write locked nodes up to but not including the entry at the
   * specified matchpoint. When we log a transactional entry, we record the LSN of the original,
   * before-this-transaction version as the abort LSN. This means that if there are multiple updates
   * to a given record in a single transaction, each update only references that original version
   * and its true predecessor.
   *
   * <p>This was done to streamline abort processing, so that an undo reverts directly to the
   * original version rather than stepping through all the intermediates. The intermediates are
   * skipped. However, undo to a matchpoint may need to stop at an intermediate point, so we need to
   * create a true chain of versions.
   *
   * <p>To do so, we read the transaction backwards from the last logged LSN to reconstruct a
   * transaction chain that links intermediate versions of records. For example, suppose our
   * transaction looks like this and that we are undoing up to LSN 250
   *
   * <p>lsn=100 node=A (version 1) lsn=200 node=B (version 1) <-- matchpointLsn lsn=300 node=C
   * (version 1) lsn=400 node=A (version 2) lsn=500 node=B (version 2) lsn=600 node=A (version 3)
   * lsn=700 node=A (version 4)
   *
   * <p>To setup the old versions, We walk from LSN 700 -> 100 700 (A) rolls back to 600 600 (A)
   * rolls back to 400 500 (B) rolls back to 200 400 (A) rolls back to 100 300 (C) rolls back to an
   * empty slot (NULL_LSN).
   *
   * <p>A partial rollback also requires resetting the lastLoggedLsn field, because these operations
   * are no longer in the btree and their on-disk entries are no longer valid.
   *
   * <p>Lastly, the appropriate write locks must be released.
   *
   * @param matchpointLsn the rollback should go up to but not include this LSN.
   */
  private void undoWrites(long matchpointLsn, List<Long> rollbackLsns) throws DatabaseException {

    /*
     * Generate a map of nodeId->List of intermediate LSNs for this node.
     * to re-create the transaction chain.
     */
    TreeLocation location = new TreeLocation();
    Long undoLsn = lastLoggedLsn;
    TxnChain chain = new TxnChain(undoLsn, id, matchpointLsn, undoDatabases, envImpl);

    try {
      while ((undoLsn != DbLsn.NULL_LSN) && DbLsn.compareTo(undoLsn, matchpointLsn) > 0) {

        UndoReader undo = new UndoReader(envImpl, undoLsn, undoDatabases);

        RevertInfo revertTo = chain.pop();

        logFinest(undoLsn, undo, revertTo);

        /*
         * When we undo this log entry, we've logically truncated
         * it from the log. Remove it from the btree and mark it
         * obsolete.
         */
        RecoveryManager.rollbackUndo(logger, Level.FINER, undo, revertTo, location, undoLsn);

        countObsoleteInexact(undoLsn, undo);
        rollbackLsns.add(undoLsn);

        /*
         * Move on to the previous log entry for this txn and update
         * what is considered to be the end of the transaction chain.
         */
        undoLsn = undo.logEntry.getUserTxn().getLastLsn();
        lastLoggedLsn = undoLsn;
      }

      /*
       * Correct the fields which hold LSN and VLSN state that may
       * now be changed.
       */
      lastApplied = chain.getLastValidVLSN();
      if (!updateLoggedForTxn()) {
        firstLoggedLsn = NULL_LSN;
      }

    } catch (DatabaseException e) {
      LoggerUtils.traceAndLogException(
          envImpl, "Txn", "undo", "For LSN=" + DbLsn.getNoFormatString(undoLsn), e);
      throw e;
    } catch (RuntimeException e) {
      throw EnvironmentFailureException.unexpectedException(
          "Txn undo for LSN=" + DbLsn.getNoFormatString(undoLsn), e);
    }

    if (lastLoggedLsn == DbLsn.NULL_LSN) {
      /*
       * The whole txn is rolled back, and it may not appear again. This
       * is the equivalent of an abort. Do any delete processing for an
       * abort which is needed.
       *
       * Set database state for deletes before releasing any write
       * locks.
       */
      setDeletedDatabaseState(false);
    }

    /* Clear any write locks that are no longer needed. */
    clearWriteLocks(chain.getRemainingLockedNodes());
  }