Cara Migrasi Orthanc dari SQLite ke MySQL

Ringkasan: Artikel ini membahas cara melakukan migrasi database Orthanc dari SQLite ke MySQL menggunakan beberapa metode yang berbeda, termasuk script Python custom dan tools bawaan Orthanc.

Pendahuluan

Orthanc adalah server DICOM open-source yang sangat populer untuk mengelola medical imaging. Secara default, Orthanc menggunakan SQLite sebagai database, namun untuk deployment yang lebih besar atau untuk kebutuhan performa yang lebih tinggi, MySQL sering menjadi pilihan yang lebih baik.

Persiapan Awal

Persyaratan Sistem

  • Server lama dengan Orthanc SQLite yang berjalan
  • Server baru dengan MySQL/MariaDB terinstal
  • Orthanc dengan plugin MySQL terinstal
  • Python 3 dengan library requests
  • Akses network antara kedua server

Instalasi Plugin MySQL

Pastikan plugin MySQL untuk Orthanc sudah terinstal di server baru:

Ubuntu/Debian
sudo apt-get install orthanc-mysql
CentOS/RHEL
sudo yum install orthanc-mysql

Konfigurasi Database MySQL

Buat database dan user untuk Orthanc:

SQL Commands
CREATE DATABASE orthanc_db;
CREATE USER 'orthanc_user'@'localhost' IDENTIFIED BY 'password_anda';
GRANT ALL PRIVILEGES ON orthanc_db.* TO 'orthanc_user'@'localhost';
FLUSH PRIVILEGES;

Konfigurasi Orthanc untuk MySQL

Edit file konfigurasi Orthanc (/etc/orthanc/orthanc.json):

orthanc.json
{
  "MySQL": {
    "EnableIndex": true,
    "EnableStorage": false,
    "Host": "localhost",
    "Port": 3306,
    "Database": "orthanc_db",
    "Username": "orthanc_user",
    "Password": "password_anda",
    "UnixSocket": "",
    "EnableSsl": false,
    "MaximumConnectionRetries": 10,
    "ConnectionTimeout": 5
  }
}

Metode Migrasi

Metode 1: Menggunakan Script Python Custom

Metode ini menggunakan REST API Orthanc untuk mengunduh dan mengupload semua DICOM instances.

migration_script.py
#!/usr/bin/env python3
"""
Script untuk migrasi data DICOM dari Orthanc SQLite ke MySQL
"""

import requests
import json
import sys
from datetime import datetime

# Konfigurasi Orthanc lama dan baru
OLD_ORTHANC_URL = 'http://192.168.1.89:8042'
NEW_ORTHANC_URL = 'http://192.168.3.14:8042'
AUTH = ('orthanc', 'orthanc')  # Username dan password Orthanc

def log_message(message):
    """Fungsi untuk logging dengan timestamp"""
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"[{timestamp}] {message}")

def check_orthanc_connection(url, name):
    """Cek koneksi ke Orthanc server"""
    try:
        response = requests.get(f"{url}/system", auth=AUTH, timeout=10)
        if response.status_code == 200:
            log_message(f"✓ Koneksi ke {name} berhasil")
            return True
        else:
            log_message(f"✗ Koneksi ke {name} gagal: HTTP {response.status_code}")
            return False
    except requests.exceptions.RequestException as e:
        log_message(f"✗ Koneksi ke {name} gagal: {e}")
        return False

def get_all_instances():
    """Ambil semua instance DICOM dari Orthanc lama"""
    try:
        response = requests.get(f"{OLD_ORTHANC_URL}/instances", auth=AUTH)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as e:
        log_message(f"Error getting instances: {e}")
        return []

def get_instance_dicom(instance_id):
    """Download DICOM file dari instance"""
    try:
        response = requests.get(f"{OLD_ORTHANC_URL}/instances/{instance_id}/file", auth=AUTH)
        response.raise_for_status()
        return response.content
    except requests.exceptions.RequestException as e:
        log_message(f"Error downloading instance {instance_id}: {e}")
        return None

def upload_dicom_to_new_orthanc(dicom_data):
    """Upload DICOM file ke Orthanc baru"""
    try:
        headers = {'Content-Type': 'application/dicom'}
        response = requests.post(f"{NEW_ORTHANC_URL}/instances", 
                               data=dicom_data, 
                               headers=headers, 
                               auth=AUTH)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as e:
        log_message(f"Error uploading DICOM: {e}")
        return None

def migrate_all_instances():
    """Migrasi semua instance DICOM"""
    log_message("=== Memulai Migrasi Orthanc ===")
    
    # Cek koneksi
    if not check_orthanc_connection(OLD_ORTHANC_URL, "Orthanc Lama"):
        return False
    if not check_orthanc_connection(NEW_ORTHANC_URL, "Orthanc Baru"):
        return False
    
    # Ambil semua instance
    log_message("Mengambil daftar semua instance...")
    instances = get_all_instances()
    
    if not instances:
        log_message("Tidak ada instance yang ditemukan")
        return False
    
    log_message(f"Total instance yang akan dimigrasi: {len(instances)}")
    
    # Migrasi setiap instance
    success_count = 0
    error_count = 0
    
    for i, instance_id in enumerate(instances, 1):
        log_message(f"Processing instance {i}/{len(instances)}: {instance_id}")
        
        # Download DICOM
        dicom_data = get_instance_dicom(instance_id)
        if dicom_data is None:
            error_count += 1
            continue
        
        # Upload ke Orthanc baru
        result = upload_dicom_to_new_orthanc(dicom_data)
        if result:
            success_count += 1
            log_message(f"✓ Berhasil upload instance {i}/{len(instances)}")
        else:
            error_count += 1
            log_message(f"✗ Gagal upload instance {i}/{len(instances)}")
    
    # Statistik akhir
    log_message("=== Migrasi Selesai ===")
    log_message(f"Berhasil: {success_count}")
    log_message(f"Gagal: {error_count}")
    
    return success_count > 0

if __name__ == "__main__":
    migrate_all_instances()

Metode 2: Menggunakan ImportDicomFiles.py (Recommended)

Metode ini menggunakan script bawaan Orthanc yang lebih stabil dan teruji.

Keunggulan Metode 2:
  • Script resmi dari Orthanc
  • Lebih stabil dan teruji
  • Handling error yang lebih baik
  • Cocok untuk dataset besar

Langkah-langkah Metode 2:

Export Data dari Server Lama

Buat script untuk export semua DICOM files:

export_script.py
#!/usr/bin/env python3
import requests
import os
from datetime import datetime

OLD_ORTHANC_URL = 'http://192.168.1.89:8042'
AUTH = ('orthanc', 'orthanc')
EXPORT_DIR = '/tmp/dicom_export'

# Buat direktori export
os.makedirs(EXPORT_DIR, exist_ok=True)

def log_message(message):
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"[{timestamp}] {message}")

try:
    # Ambil semua instances
    response = requests.get(f'{OLD_ORTHANC_URL}/instances', auth=AUTH)
    response.raise_for_status()
    instances = response.json()
    
    log_message(f'Total instances: {len(instances)}')
    
    for i, instance_id in enumerate(instances, 1):
        if i % 100 == 0:
            log_message(f'Progress: {i}/{len(instances)}')
        
        # Download DICOM file
        response = requests.get(f'{OLD_ORTHANC_URL}/instances/{instance_id}/file', auth=AUTH)
        response.raise_for_status()
        
        # Simpan ke file
        with open(f'{EXPORT_DIR}/{instance_id}.dcm', 'wb') as f:
            f.write(response.content)
    
    log_message('Export selesai!')
    
except Exception as e:
    log_message(f'Error: {e}')

Transfer ke Server Baru

Transfer Commands
# Compress dan transfer
tar -czf dicom_files.tar.gz /tmp/dicom_export/
scp dicom_files.tar.gz user@new-server:/tmp/

# Di server baru
cd /tmp
tar -xzf dicom_files.tar.gz

Import ke MySQL Orthanc

Jalankan ImportDicomFiles.py di server baru:

Import Command
# Pastikan server MySQL Orthanc sudah berjalan
sudo systemctl start orthanc

# Import menggunakan script Orthanc
python3 /usr/share/doc/orthanc/Samples/ImportDicomFiles/ImportDicomFiles.py \
    192.168.3.14 8042 /tmp/dicom_export/ orthanc orthanc

Script Otomatis Lengkap

Untuk mempermudah proses, berikut adalah script bash yang mengotomatisasi seluruh proses:

complete_migration.sh
#!/bin/bash

# Script lengkap untuk migrasi Orthanc SQLite ke MySQL

set -e

# Konfigurasi
OLD_ORTHANC_HOST="192.168.1.89"
NEW_ORTHANC_HOST="192.168.3.14"
ORTHANC_PORT="8042"
ORTHANC_USER="orthanc"
ORTHANC_PASSWORD="orthanc"
EXPORT_DIR="/tmp/dicom_export"
LOG_FILE="/var/log/orthanc_migration.log"

# Fungsi logging
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

# Fungsi untuk export dari server lama
export_from_old_server() {
    log "=== Memulai Export dari Server Lama ==="
    
    mkdir -p "$EXPORT_DIR"
    
    python3 << EOF
import requests
import os
from datetime import datetime

OLD_ORTHANC_URL = 'http://${OLD_ORTHANC_HOST}:${ORTHANC_PORT}'
AUTH = ('${ORTHANC_USER}', '${ORTHANC_PASSWORD}')
EXPORT_DIR = '${EXPORT_DIR}'

def log_message(message):
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"[{timestamp}] {message}")

try:
    # Cek koneksi
    response = requests.get(f'{OLD_ORTHANC_URL}/system', auth=AUTH, timeout=10)
    if response.status_code != 200:
        log_message(f"ERROR: Tidak dapat terhubung ke Orthanc lama")
        exit(1)
    
    log_message("✓ Terhubung ke Orthanc lama")
    
    # Ambil semua instances
    response = requests.get(f'{OLD_ORTHANC_URL}/instances', auth=AUTH)
    instances = response.json()
    
    log_message(f"Total instances: {len(instances)}")
    
    for i, instance_id in enumerate(instances, 1):
        if i % 100 == 0:
            log_message(f"Progress: {i}/{len(instances)}")
        
        try:
            response = requests.get(f'{OLD_ORTHANC_URL}/instances/{instance_id}/file', auth=AUTH)
            response.raise_for_status()
            
            with open(f'{EXPORT_DIR}/{instance_id}.dcm', 'wb') as f:
                f.write(response.content)
                
        except Exception as e:
            log_message(f"ERROR: {e}")
    
    log_message("Export selesai!")
    
except Exception as e:
    log_message(f"ERROR: {e}")
    exit(1)
EOF

    tar -czf "${EXPORT_DIR}.tar.gz" -C "$(dirname "$EXPORT_DIR")" "$(basename "$EXPORT_DIR")"
    log "Export selesai: ${EXPORT_DIR}.tar.gz"
}

# Fungsi untuk import ke server baru
import_to_new_server() {
    log "=== Memulai Import ke Server Baru ==="
    
    if [ ! -f "${EXPORT_DIR}.tar.gz" ]; then
        log "ERROR: Archive tidak ditemukan"
        exit 1
    fi
    
    tar -xzf "${EXPORT_DIR}.tar.gz" -C "$(dirname "$EXPORT_DIR")"
    
    IMPORT_SCRIPT="/usr/share/doc/orthanc/Samples/ImportDicomFiles/ImportDicomFiles.py"
    if [ ! -f "$IMPORT_SCRIPT" ]; then
        log "ERROR: ImportDicomFiles.py tidak ditemukan"
        exit 1
    fi
    
    log "Memulai import..."
    python3 "$IMPORT_SCRIPT" "$NEW_ORTHANC_HOST" "$ORTHANC_PORT" "$EXPORT_DIR" "$ORTHANC_USER" "$ORTHANC_PASSWORD"
    
    log "Import selesai"
}

# Menu utama
case "${1:-}" in
    "export")
        export_from_old_server
        ;;
    "import")
        import_to_new_server
        ;;
    *)
        echo "Usage: $0 {export|import}"
        exit 1
        ;;
esac

Cara Penggunaan

Opsi 1: Script Python Custom

# Simpan script dan jalankan
python3 migration_script.py

Opsi 2: ImportDicomFiles.py (Recommended)

Di server lama
# 1. Export data
./complete_migration.sh export

# 2. Transfer ke server baru
scp /tmp/dicom_export.tar.gz user@new-server:/tmp/
Di server baru
# 3. Import data
./complete_migration.sh import

Opsi 3: Manual One-liner

Export dari server lama
python3 -c "
import requests
import os
os.makedirs('/tmp/dicom_export', exist_ok=True)
instances = requests.get('http://192.168.1.89:8042/instances', auth=('orthanc', 'orthanc')).json()
for i, instance_id in enumerate(instances):
    print(f'Exporting {i+1}/{len(instances)}: {instance_id}')
    data = requests.get(f'http://192.168.1.89:8042/instances/{instance_id}/file', auth=('orthanc', 'orthanc')).content
    with open(f'/tmp/dicom_export/{instance_id}.dcm', 'wb') as f:
        f.write(data)
"
Import ke server baru
python3 /usr/share/doc/orthanc/Samples/ImportDicomFiles/ImportDicomFiles.py \
    192.168.3.14 8042 /tmp/dicom_export/ orthanc orthanc

Verifikasi Hasil Migrasi

Setelah migrasi selesai, lakukan verifikasi dengan perintah berikut:

Verifikasi Commands
# Cek statistik database
curl -u orthanc:orthanc http://192.168.3.14:8042/statistics

# Cek database backend
curl -u orthanc:orthanc http://192.168.3.14:8042/system

# Cek jumlah data
curl -u orthanc:orthanc http://192.168.3.14:8042/patients | jq length
curl -u orthanc:orthanc http://192.168.3.14:8042/studies | jq length
curl -u orthanc:orthanc http://192.168.3.14:8042/instances | jq length

Tips dan Troubleshooting

Untuk Dataset Besar

  • Gunakan ImportDicomFiles.py untuk stabilitas yang lebih baik
  • Monitor penggunaan disk space selama proses
  • Pertimbangkan untuk melakukan migrasi secara bertahap per patient atau study
  • Gunakan compression untuk transfer file yang lebih cepat

Optimasi Performa

  • Sesuaikan konfigurasi MySQL untuk dataset DICOM yang besar
  • Gunakan SSD untuk storage yang lebih cepat
  • Tingkatkan innodb_buffer_pool_size di MySQL
  • Disable MySQL binary logging selama import untuk performa lebih baik

Common Issues

Masalah Umum dan Solusi:
  • Connection refused: Pastikan firewall tidak memblokir port 8042 dan 3306
  • MySQL connection error: Cek konfigurasi database dan credential
  • Out of disk space: Monitor space disk selama proses export/import
  • Timeout errors: Tingkatkan timeout value di konfigurasi

Kesimpulan

Migrasi Orthanc dari SQLite ke MySQL dapat dilakukan dengan beberapa metode. Metode menggunakan ImportDicomFiles.py adalah yang paling direkomendasikan karena:

  • Merupakan bagian resmi dari Orthanc
  • Sudah teruji untuk berbagai skenario
  • Memiliki error handling yang baik
  • Cocok untuk dataset besar

Pastikan untuk selalu melakukan backup data sebelum memulai proses migrasi dan verifikasi hasil migrasi dengan teliti sebelum menggunakan sistem di production.

Posting Komentar