Automated DB and Backup File Transfer

IT EngineersDevelopers

Getting Started

Core Concepts

Automatically transfer DB dumps and backup/archive files to designated locations

Databases and backup programs generate various files, including DB dumps, full backup files, incremental backup files, and log archives.

Automated DB and backup file transfer checks for generated files and automatically transfers them to remote data centers or cloud storage according to configured conditions.

By connecting the location where files are generated with the remote storage environment, regularly generated files can be managed through a defined flow.

text
DB · Backup System
       │
       ▼
   Check for File Creation
       │
       ▼
   Check Transfer Conditions
       │
       ▼
  Remote Storage Environment
       │
       ├── Remote Data Center
       │
       └── Cloud Storage

Automation Flow

Connect the workflow from backup file creation through remote storage and result confirmation in sequence

When a backup file is created or a backup task is completed, the next transfer task can run according to the configured conditions.

You can also configure a flow that checks files generated on a schedule and transfers them to the remote storage environment.

① Create DB Dump or Backup File

② Confirm File or Backup Completion

③ Run Transfer Task

④ Store in Remote Environment

⑤ Check Results

This flow automatically connects backup file creation and remote storage in the required sequence.

Operational Benefits

Manage regular backup file transfers and remote storage as a single workflow

DB and backup environments continuously manage file creation, remote transfer, storage locations, and execution results.

By configuring an automated transfer flow, regularly generated files can be transferred to designated remote environments while execution results and storage status are reviewed together.

CategoryIndividual ManagementAutomated Transfer
File CheckCheck generated files for each taskCheck according to configured conditions
Transfer ExecutionRun manually for each fileRun automatically according to conditions and schedule
Storage LocationSpecify the destination for each taskConfigure paths by file type
Result ManagementReview results by taskReview execution records and storage results together

This lets you configure the entire workflow from DB dump and backup file creation through remote storage as a single operational flow.

IT Engineers

Source Connection

Connect file creation locations on database and backup systems

First, connect the systems and folders where DB dumps and backup files are created to the transfer environment.

Specify file paths generated on database servers, backup servers, or storage systems and configure them as transfer sources.

Source EnvironmentGenerated Files
Database ServerDB dumps and export files
Backup ServerFull and incremental backup files
Log ServerTransaction logs and archives
StorageFiles intended for long-term retention

Transfer Configuration

Configure the remote storage location and execution conditions as a single transfer flow

After connecting the source files, configure a remote data center or cloud storage as the transfer destination.

You can configure transfer tasks to start when a file is created, when a backup task is completed, or according to a defined schedule.

Configuration ItemConfiguration
Remote DestinationData center or cloud storage
Storage LocationFolder or bucket path by file type
Execution ConditionsFile creation, backup completion, scheduled execution
File TypeDB Dump, Backup, Archive, etc.

For example, you can transfer daily backups to cloud storage while sending weekly backups and long-term retention files to separate remote paths.

text
Backup Task
    │
    ▼
Backup Complete
    │
    ▼
Check Files
    │
    ├── DB Dump ────────────→ Remote Data Center
    │
    ├── Daily Backup ───────→ Cloud Storage
    │
    └── Archive ────────────→ Long-Term Retention Path

Automated Transfer

Automatically transfer generated backup files to designated remote environments

When the configured conditions are met, transfer DB dumps and backup files to the designated remote environment.

You can branch files to different storage locations based on file type and path, or transfer a single backup file to multiple remote environments at the same time.

text
                Backup File
                    │
                    ▼
                Transfer Task
              ╱           ╲
             ▼             ▼
      Remote Data Center   Cloud Storage
             │             │
             ▼             ▼
        Backup Storage      Bucket / Path

Result Management

Check transfer status and storage results, then rerun tasks when necessary

When a transfer task runs, use Runs and task details to review file processing status and remote storage results.

When a specific task requires additional review, use the Activity Log and execution information to check the source file, system connection, destination path, and access scope before rerunning the required task.

text
Run Transfer
    │
    ▼
Check Task Status
    │
    ├── Running
    │      │
    │      └── Check Progress
    │
    ├── Completed
    │      │
    │      └── Check Storage Result
    │
    └── Review Required
           │
           ▼
       Review Detailed Records
           │
           ▼
 Check Source · Connection · Storage Path
           │
           ▼
       Rerun Required Task
           │
           ▼
        Check Result Again
Review ItemDetails
Source FileTransferred DB dumps and backup files
DestinationRemote server or cloud storage path
Progress StatusCurrent task status and progress
Processing ResultNumber of transferred files and total size
Execution RecordFile processing stages and task results

Developers

Transfer the dump to a remote location when the backup completes and verify it with a checksum

Integration Preparation

Prepare shared request code and path conventions

import os
import requests

BASE_URL = os.getenv("INNORIX_BASE_URL", "https://app.innorix.com").rstrip("/")
TOKEN = os.environ["INNORIX_ACCESS_TOKEN"]
WORKSPACE_ID = os.getenv("INNORIX_WORKSPACE_ID")   # optional; falls back to the current workspace

STATUS_COMPLETE = 2
TERMINAL = {2, 4, 5, 9, 99}          # complete / error / cancelled / partial / failed
NOT_SUCCEEDED = {4, 5, 9, 99}


def api(method, path, body=None, params=None):
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {TOKEN}",
    }

    if WORKSPACE_ID:
        headers["x-workspace-id"] = WORKSPACE_ID

    response = requests.request(
        method, BASE_URL + path,
        headers=headers, json=body, params=params, timeout=30,
    )

    payload = response.json() if response.content else {}

    if not response.ok:
        raise RuntimeError(payload.get("message") or f"HTTP {response.status_code}")

    return payload.get("data")


def is_terminal(detail):
    return detail.get("isTerminal", detail.get("status") in TERMINAL)
import base64
import time


def encode_path(device_id, raw_path):
    normalized = str(raw_path or "").replace("\\", "/")
    token = base64.b64encode(normalized.encode("utf-8")).decode("ascii")
    return f"{device_id}_ino_{token}"


def now_iso():
    return time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime())

Determine the transfer status using the values below. There are five terminal states, and the successful state is Complete (2).

Status ValueMeaningTerminal
2CompleteYes
4ErrorYes
5CancelledYes
9Partially CompleteYes
99FailedYes
1 · 6 · 12 · 13Started · Transferring · Synchronizing · ReceivingNo

Transfer After Backup Completion

Continue the transfer at the end of the backup script

Calling the transfer immediately after the backup program creates the file lets you align the transfer precisely with the point when the file is ready.

When sending files, explicitly set isDir: false in sourceItem. sourcePaths treats every path as a folder, so providing a dump file can cause the server to scan it as a folder, resulting in slowdowns or timeouts.

import glob
import os


def latest_backup(directory, pattern="*.dump"):
    files = glob.glob(os.path.join(directory, pattern))

    if not files:
        raise FileNotFoundError(directory)

    return max(files, key=os.path.getmtime)


def send_backup(source, target, backup_path, target_path):
    transfer = api("POST", "/api/transfers/manual", {
        "sourceDevice": source,
        "targetDevice": target,
        "targetPath": target_path,
        "sourceItem": [{
            "path": backup_path,
            "isDir": False,
            "fileSize": os.path.getsize(backup_path),
        }],
        "sendAllFolder": False,
        "checkIntegrity": True,
        "transferOptions": {"target-action": "numbering"},
    })

    return transfer["monitorId"]

Backup files must retain their run history, so set the destination policy to numbering. With overwrite, previous backups disappear and you cannot choose the recovery point.

Providing the file size as well makes the operation faster because the server does not need to retrieve the size of each item again.

Because a corrupted backup may not be discovered until recovery is attempted, use checkIntegrity to verify integrity during the transfer stage.

Branch by File Type

Send dumps, incrementals, and archives to different locations

A single transfer handles one destination. When destinations differ by file type, create separate transfers.

ROUTES = {
    "dump":    ("device-dc-01", "/backup/dump"),
    "daily":   ("device-cloud-01", "/backup/daily"),
    "archive": ("device-archive-01", "/backup/archive"),
}


def classify(filename):
    name = os.path.basename(filename).lower()

    if name.endswith(".dump"):
        return "dump"

    if "archive" in name or name.endswith(".tar.gz"):
        return "archive"

    return "daily"


def dispatch(source, files):
    transfers = {}

    for path in files:
        target, target_path = ROUTES[classify(path)]
        transfers[path] = send_backup(source, target, path, target_path)

    return transfers

When a single backup file must be sent to multiple remote environments, create a transfer for each destination and store the returned monitorId for each destination so the individual transfers can be queried later.

copies = {
    target: send_backup("device-db-01", target, backup_path, path)
    for target, path in [("device-dc-01", "/backup/dump"),
                         ("device-cloud-01", "/backup/mirror")]
}

Schedule Automation

Register the task to run repeatedly at a defined time

If the backup script cannot be modified, use schedule automation instead.

def build_schedule_automation(name, source, source_path, target, target_path,
                              schedule):
    return {
        "name": name,
        "flowName": name,
        "transferType": "normal",
        "timezone": "Asia/Seoul",
        "step": 1,
        "isUpcoming": False,
        "details": [
            {
                "senderId": source,
                "receiverId": target,
                "sourceItem": [
                    {
                        "hash": encode_path(source, source_path),
                        "filePath": source_path,
                        "isDir": True,
                    }
                ],
                "targetPath": encode_path(target, target_path),
                "step": 1,
                "transferOptions": {
                    "noSchedule": False,
                    "target-action": "numbering",
                    "send-fileoption": {},
                },
            }
        ],
        "schedules": [schedule],
    }


DAILY_3AM = {
    "type": "day",
    "startDateType": "now",
    "hour": "03",
    "minute": "00",
    "ampm": "am",
    "startDate": now_iso(),
    "timezone": "Asia/Seoul",
}

MONTHLY = {
    "type": "month",
    "startDateType": "now",
    "day": "1",
    "hour": "04",
    "minute": "00",
    "ampm": "am",
    "startDate": now_iso(),
    "timezone": "Asia/Seoul",
}

api("POST", "/api/automations", build_schedule_automation(
    "daily backup", "device-db-01", "/backup",
    "device-dc-01", "/backup/daily", DAILY_3AM))

There are four requirements that must always be followed when creating an automation request.

ItemHow to Configure
isUpcomingMust be false. The server default of true ignores the schedule in the request and replaces it with a one-time five-minute schedule. A stage with triggerAutomation forces the value to false, so you only need to set it directly on the first stage without a trigger
stepSet it in both the top level and details. It indicates the hop position within the workflow
sourceItemInclude both hash (the path token) and filePath (the plain-text path)
syncTypeSet it inside transferOptions. 1 is one-way and 2 is two-way

Registration succeeds even if all four items are omitted, but the behavior changes at execution time. If a recurring schedule was registered but runs only once, check isUpcoming first.

Prevent Duplicate Registrations

Prevent the same backup task from being created twice

Even if an automation with the same name already exists, a new one is created. If a batch is retried, the same backup is transferred twice, and because the destination policy is numbering, two copies of the file accumulate.

def find_automation(name):
    # the name we send is stored as flowName in the response
    # automationName is a server generated id like T4037-8500-1815, not the name we set.
    for page in range(1, 6):
        result = api("GET", "/api/automations",
                     params={"page": page, "size": 100, "search": name}) or {}

        items = [item
                 for flow in result.get("automations") or []
                 for item in flow.get("automations") or []]

        for item in items:
            if item.get("flowName") == name:
                return item

        if len(items) < 100:
            return None

    return None

The automation list is returned nested by flow group, so you must iterate through the inner arrays as well. Server search uses partial matching, so select only the item whose name exactly matches from the returned results.

Integrity Verification

Verify that the backup stored remotely matches the source

def verify(monitor_id, timeout=1800, interval=10):
    api("POST", f"/api/transfers/{monitor_id}/verification", {})

    deadline = time.time() + timeout

    while time.time() < deadline:
        result = api("GET", f"/api/transfers/{monitor_id}/verification") or {}

        if result.get("verified"):
            return result

        time.sleep(interval)

    raise TimeoutError(monitor_id)
result = verify(monitor_id)

if not result["checksumMatched"]:
    alert(f"backup integrity failed: {result.get('mismatchedFiles')}")
Response ItemDetails
checksumAlgorithmChecksum algorithm used
sourceFileCount · targetFileCountSource and remote file counts
checksumMatchedWhether the checksums match
mismatchedCountNumber of mismatches

If the file counts differ, the transfer is incomplete. If the counts match but there are mismatches, the contents are corrupted. The latter is more dangerous for backups, so report verification failures immediately.

Verify Results and Retransmit

Check transfer results and retransmit failed files

def wait(monitor_id, timeout=3600, interval=3):
    deadline = time.time() + timeout

    while time.time() < deadline:
        detail = api("GET", f"/api/transfers/{monitor_id}")

        if is_terminal(detail):
            return detail

        time.sleep(interval)

    raise TimeoutError(monitor_id)


def failed_files(monitor_id):
    result = api("GET", f"/api/transfers/{monitor_id}/files", params={
        "state": "any", "size": 500,
    }) or {}

    return [r for r in (result.get("children") or [])
            if r.get("status") in NOT_SUCCEEDED]


def retry_failed(monitor_id):
    rows = failed_files(monitor_id)

    if not rows:
        return 0

    api("POST", f"/api/transfers/{monitor_id}/retry", {
        "filesRetry": [
            {"filePath": r["sourceFilePath"], "isDir": bool(r.get("isFolder"))}
            for r in rows
        ]
    })

    return len(rows)
detail = wait(monitor_id, timeout=7200)

if detail["status"] != STATUS_COMPLETE:
    alert(f"backup transfer failed - retried {retry_failed(monitor_id)} files")

To check whether the previous run of a daily task ended successfully, query recent history over a defined period.

from datetime import datetime, timedelta, timezone


def paginate(path, params=None, limit=200, max_pages=50):
    query = dict(params or {})
    query["limit"] = limit
    cursor = None

    for _ in range(max_pages):
        if cursor:
            query["cursor"] = cursor

        result = api("GET", path, params=query) or {}

        for record in result.get("data") or []:
            yield record

        pagination = result.get("pagination") or {}

        if not pagination.get("hasMore"):
            return

        cursor = pagination.get("nextCursor")

        if not cursor:
            return


end = datetime.now(timezone.utc)
fmt = "%Y-%m-%dT%H:%M:%SZ"

rows = list(paginate("/api/transfer-history", params={
    "startDate": (end - timedelta(days=1)).strftime(fmt),
    "endDate": end.strftime(fmt),
}))

failures = [r for r in rows if r.get("status") in NOT_SUCCEEDED]

if failures:
    alert(f"{len(failures)} backup transfers failed yesterday")

When the complete history is needed as an audit file, use CSV export.

params = {
    "periodDays": 30,
    "page": 1,
    "size": 10000,
    "filter": "[]",              # the server parses this as a JSON string, so send an empty array
    "sort": "startDate:desc",
}

Even when there are no conditions, send an empty array string ("[]") in filter because the server parses this value as JSON. The fields allowed in sort are status, sourceDeviceName, targetDeviceName, totalSize, sourceFileCount, startDate, endDate, automationName, formattedTransferTime, and savedTime.

Review ItemDetails
SourceTransferred dumps and backup files
DestinationRemote data center or cloud path
VerificationWhether file counts and checksums match
StatusTransfer status and success
HistoryExecution result for the most recent run
RetransmissionFailed files and processing result