All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.orientechnologies.agent.services.backup.strategy.OBackupStrategy Maven / Gradle / Ivy

/*
 * Copyright 2015 OrientDB LTD (info(at)orientdb.com)
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 *   For more information: http://www.orientdb.com
 */

package com.orientechnologies.agent.services.backup.strategy;

import com.orientechnologies.agent.services.backup.OBackupConfig;
import com.orientechnologies.agent.services.backup.OBackupListener;
import com.orientechnologies.agent.services.backup.OBackupTask;
import com.orientechnologies.agent.services.backup.log.*;
import com.orientechnologies.backup.uploader.OLocalBackupUploader;
import com.orientechnologies.backup.uploader.OUploadMetadata;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.enterprise.server.OEnterpriseServer;
import com.orientechnologies.orient.core.db.ODatabaseDocumentInternal;
import com.orientechnologies.orient.core.db.document.ODatabaseDocument;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.server.handler.OAutomaticBackup;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Calendar;
import java.util.Date;
import java.util.Optional;
import java.util.function.Consumer;

/** Created by Enrico Risa on 25/03/16. */
public abstract class OBackupStrategy {

  protected ODocument cfg;
  protected OBackupLogger logger;
  protected Optional uploader;

  public OBackupStrategy(ODocument cfg, OBackupLogger logger) {
    this.cfg = cfg;
    this.logger = logger;
    this.uploader = OLocalBackupUploader.from(cfg.field("upload"));
  }

  public OBackupStartedLog startBackup() throws IOException {
    final OBackupLog last = logger.findLast(OBackupLogType.BACKUP_SCHEDULED, getUUID());

    long txId;
    long unitId;
    if (last != null) {
      unitId = last.getUnitId();
      txId = last.getTxId();
    } else {
      unitId = logger.nextOpId();
      txId = logger.nextOpId();
    }
    return new OBackupStartedLog(unitId, txId, getUUID(), getDbName(), getMode().toString());
  }

  public OBackupFinishedLog endBackup(long unitId, long opsId) {
    return new OBackupFinishedLog(unitId, opsId, getUUID(), getDbName(), getMode().toString());
  }

  // Backup
  public void doBackup(final OBackupListener listener) throws IOException {
    final OBackupStartedLog start = startBackup();
    logger.log(start);
    listener.onEvent(cfg, start);

    try {
      final OBackupFinishedLog end = doBackup(start);
      final OBackupFinishedLog finishedLog = (OBackupFinishedLog) logger.log(end);
      listener.onEvent(cfg, finishedLog);
      doUpload(listener, finishedLog);
    } catch (final Exception e) {
      final OBackupErrorLog error =
          new OBackupErrorLog(
              start.getUnitId(), start.getTxId(), getUUID(), getDbName(), getMode().toString());
      final StringWriter sw = new StringWriter();
      error.setMessage(e.getMessage());
      error.setStackTrace(sw.toString());
      logger.log(error);
      listener.onEvent(cfg, error);
      return;
    }
  }

  public void doUpload(final OBackupListener listener, final OBackupFinishedLog log) {
    uploader.ifPresent(
        (uploader) -> {
          final OBackupUploadStartedLog uploadStarted =
              new OBackupUploadStartedLog(
                  log.getUnitId(), log.getTxId(), getUUID(), getDbName(), getMode().toString());
          uploadStarted.setFileName(log.getFileName());
          uploadStarted.setPath(log.getPath());
          logger.log(uploadStarted);

          final String[] fragments = log.getPath().split(File.separator);
          try {
            final OUploadMetadata metadata =
                uploader.executeUpload(
                    log.getPath() + File.separator + log.getFileName(),
                    log.getFileName(),
                    fragments[fragments.length - 1]);
            final OBackupUploadFinishedLog finishedLog =
                new OBackupUploadFinishedLog(
                    uploadStarted.getUnitId(),
                    uploadStarted.getTxId(),
                    getUUID(),
                    getDbName(),
                    getMode().toString());
            finishedLog.setElapsedTime(metadata.getElapsedTime());
            finishedLog.setFileSize(log.getFileSize());
            finishedLog.setFileName(log.getFileName());
            finishedLog.setMetadata(metadata.getMetadata());
            finishedLog.setUploadType(metadata.getType());
            final OBackupUploadFinishedLog uploadLog =
                (OBackupUploadFinishedLog) logger.log(finishedLog);
            log.setUpload(uploadLog);
            logger.updateLog(log);

            listener.onEvent(cfg, uploadLog);
          } catch (final Exception e) {
            final OBackupUploadErrorLog error =
                new OBackupUploadErrorLog(
                    log.getUnitId(), log.getTxId(), getUUID(), getDbName(), getMode().toString());
            final StringWriter sw = new StringWriter();
            error.setMessage(e.getMessage());
            error.setStackTrace(sw.toString());
            logger.log(error);
          }
        });
  }

  public void doRestore(final OBackupListener listener, ODocument doc) {
    final String databaseName = doc.field("target");
    final long unitId = doc.field("unitId");
    ODatabaseDocument database = null;
    if (logger.getServer().existsDatabase(databaseName)) {
      throw new IllegalArgumentException(
          "Cannot restore the backup to an existing database (" + databaseName + ").");
    }
    try {
      final OBackupFinishedLog finished =
          (OBackupFinishedLog) logger.findLast(OBackupLogType.BACKUP_FINISHED, getUUID(), unitId);
      validateBackup(finished);
      new Thread(() -> startRestoreBackup(logger.getServer(), finished, databaseName, listener))
          .start();
    } catch (final IOException e) {
      OLogManager.instance().error(this, "Error finding backup log for " + getUUID(), e);
    }
  }

  private void validateBackup(final OBackupFinishedLog finished) {
    if (finished.getUpload() == null) {
      final File f = new File(finished.getPath());
      if (!f.exists()) {
        throw new IllegalArgumentException(
            "Cannot restore the backup from path (" + finished.getPath() + ").");
      }
    }
  }

  private void startRestoreBackup(
      OEnterpriseServer server,
      OBackupFinishedLog finished,
      String databaseName,
      OBackupListener listener) {
    ORestoreStartedLog restoreStartedLog = null;
    try {

      restoreStartedLog =
          new ORestoreStartedLog(
              finished.getUnitId(), logger.nextOpId(), getUUID(), getDbName(), finished.getMode());
      logger.log(restoreStartedLog);

      listener.onEvent(cfg, restoreStartedLog);

      if (finished.getUpload() != null) {
        OBackupUploadFinishedLog upload = finished.getUpload();
        ORestoreStartedLog finalRestoreStartedLog = restoreStartedLog;
        uploader.ifPresent(
            (u) -> {
              String path = u.executeDownload(upload);
              doRestore(
                  server,
                  finished,
                  path,
                  databaseName,
                  listener,
                  finalRestoreStartedLog,
                  (log) -> {
                    log.setPath(path);
                    log.setMetadata(upload.getMetadata());
                  });
            });
      } else {
        doRestore(
            server,
            finished,
            finished.getPath(),
            databaseName,
            listener,
            restoreStartedLog,
            (log) -> {
              log.setPath(finished.getPath());
            });
      }

    } catch (Exception e) {

      if (restoreStartedLog != null) {
        ORestoreErrorLog error =
            new ORestoreErrorLog(
                restoreStartedLog.getUnitId(),
                restoreStartedLog.getTxId(),
                getUUID(),
                getDbName(),
                getMode().toString());
        error.setMessage(e.getMessage());
        logger.log(error);
        listener.onEvent(cfg, error);
      }
    }
  }

  private void doRestore(
      OEnterpriseServer server,
      OBackupFinishedLog finished,
      String path,
      String databaseName,
      OBackupListener listener,
      ORestoreStartedLog restoreStartedLog,
      Consumer consumer) {
    server.restore(databaseName, path);
    ORestoreFinishedLog finishedLog =
        new ORestoreFinishedLog(
            restoreStartedLog.getUnitId(),
            restoreStartedLog.getTxId(),
            getUUID(),
            getDbName(),
            restoreStartedLog.getMode());

    finishedLog.setElapsedTime(
        finishedLog.getTimestamp().getTime() - restoreStartedLog.getTimestamp().getTime());
    finishedLog.setTargetDB(databaseName);
    finishedLog.setRestoreUnitId(finished.getUnitId());
    consumer.accept(finishedLog);

    logger.log(finishedLog);

    listener.onEvent(cfg, finishedLog);
  }

  protected OBackupFinishedLog doBackup(final OBackupStartedLog start) {
    ODatabaseDocument db = null;
    try {
      db = getDatabase();
      db.activateOnCurrentThread();

      final String path = calculatePath(db);
      final String fName = db.incrementalBackup(path);
      final OBackupFinishedLog end = endBackup(start.getUnitId(), start.getTxId());
      end.setFileName(fName);
      end.setPath(path);
      end.setFileSize(calculateFileSize(path + File.separator + fName));
      end.setElapsedTime(end.getTimestamp().getTime() - start.getTimestamp().getTime());
      return end;
    } finally {
      if (db != null) {
        db.close();
      }
    }
  }

  protected long calculateFileSize(String path) {
    File f = new File(path);
    return f.length();
  }

  public abstract OAutomaticBackup.MODE getMode();

  protected abstract String calculatePath(ODatabaseDocument db);

  public abstract Date scheduleNextExecution(OBackupListener listener);

  public OBackupLogger getLogger() {
    return logger;
  }

  protected ODatabaseDocument getDatabase() {
    final String dbName = cfg.field(OBackupConfig.DBNAME);
    final OEnterpriseServer server = logger.getServer();

    final ODatabaseDocumentInternal db = server.getDatabases().openNoAuthenticate(dbName, null);
    return db;
  }

  public String getUUID() {
    return cfg.field(OBackupConfig.ID);
  }

  public String getDbName() {
    return cfg.field(OBackupConfig.DBNAME);
  }

  public Boolean isEnabled() {
    return cfg.field(OBackupConfig.ENABLED);
  }

  public Integer getRetentionDays() {
    return cfg.field(OBackupConfig.RETENTION_DAYS);
  }

  protected OBackupScheduledLog lastUnfiredSchedule() {
    OBackupLog lastSchedule = null;
    try {
      lastSchedule = logger.findLast(OBackupLogType.BACKUP_SCHEDULED, getUUID());
      if (lastSchedule != null) {
        final OBackupLog lastBackup = logger.findLast(OBackupLogType.BACKUP_FINISHED, getUUID());
        if (lastBackup != null && lastBackup.getTxId() == lastSchedule.getTxId()) {
          lastSchedule = null;
          OLogManager.instance()
              .debug(
                  this,
                  "Last backup not null, but set to null due to equal TX ids: "
                      + lastBackup.getMode());
        }
      }
    } catch (final Exception e) {
      OLogManager.instance()
          .error(this, "Error finding last unfired schedule for UUID : " + getUUID(), e);
    }
    return (OBackupScheduledLog) lastSchedule;
  }

  public void doDeleteBackup(OBackupTask oBackupTask, Long unitId, Long tx) {
    try {
      logger.deleteByUUIDAndUnitIdAndTx(getUUID(), unitId, tx);
    } catch (IOException e) {
      OLogManager.instance().error(this, "Error deleting backups for UUID : " + getUUID(), e);
    }
  }

  public void retainLogs() {
    final Integer retentionDays = getRetentionDays();
    if (retentionDays != null && retentionDays > 0) {
      retainLogs(retentionDays);
    }
  }

  public void retainLogs(int retentionDays) {
    Calendar c = Calendar.getInstance();
    c.setTime(new Date());
    c.add(Calendar.DATE, (-1) * retentionDays);

    Long time = c.getTime().getTime();
    try {
      logger.deleteByUUIDAndTimestamp(getUUID(), time);
    } catch (IOException e) {
      OLogManager.instance().error(this, "Error deleting backups for UUID : " + getUUID(), e);
    }
  }

  public ODocument getCfg() {
    return cfg;
  }

  public void deleteLastScheduled() {

    OBackupScheduledLog scheduled = lastUnfiredSchedule();
    if (scheduled != null) {
      logger.deleteLog(scheduled);
    }
  }

  @Override
  public boolean equals(Object obj) {
    return this.getClass().isInstance(obj);
  }

  @Override
  public int hashCode() {
    return 1;
  }

  public void markLastBackup() {

    try {
      OBackupFinishedLog lastCompleted =
          (OBackupFinishedLog) logger.findLast(OBackupLogType.BACKUP_FINISHED, getUUID());

      if (lastCompleted != null) {
        lastCompleted.setPrevChange(true);
        logger.updateLog(lastCompleted);
      }

    } catch (IOException e) {
      OLogManager.instance().error(this, "Error updating lob backups for UUID : " + getUUID(), e);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy