Restore Email Tags di Zimbra Pasca Migrasi Menggunakan Imapsync

Pernahkah rekan-rekan mengalami situasi pasca migrasi mail server Zimbra Collaboration Suite (ZCS), di mana seluruh proses pemindahan email menggunakan imapsync dinyatakan sukses, namun selang beberapa waktu pengguna melaporkan bahwa seluruh folder Tag (label kategori email) di Webmail kosong (berisi 0 email)?

Kondisi ini sering kali membingungkan tim Technical Support atau System Administrator. Struktur nama tag dan warna mungkin sudah dibuat ulang di server baru, namun ketika tag tersebut diklik oleh user di Zimbra Web Client (ZWC), daftar emailnya nihil. Pencarian melalui CLI dengan perintah zmmailbox search -t message "tag:<Nama Tag>" pun menghasilkan num: 0.

Jika server lama sudah di-decommission atau dimatikan, opsi migrasi ulang tentu bukan pilihan. Untungnya, jika kita memahami cara kerja internal database Zimbra, kita tidak perlu panik. Metadata nama tag sebenarnya masih selamat 100% di database lokal server baru! Pada artikel kali ini, saya akan membagikan panduan teknis langkah demi langkah bagaimana memulihkan ribuan relasi tag email tersebut secara otomatis, cepat (hanya dalam hitungan detik), dan aman tanpa downtime.

Memahami Mengapa Email Tag Bisa “Hilang” Pasca Migrasi

Sebelum masuk ke langkah pemulihan, ada dua faktor teknis utama di balik fenomena ini:

  1. Keterbatasan Protokol IMAP (RFC 3501): Spesifikasi standar IMAP mendefinisikan flag/keyword pesan sebagai token bertipe atom. Token ini melarang penggunaan karakter spasi dan karakter khusus. Ketika imapsync mentransfer email, tag tanpa spasi (misalnya FINANCE atau VENDOR) dapat ditransfer dengan mulus. Namun, tag yang memiliki spasi (seperti 00. KNOWLEDGE BASE atau 01. PROPOSAL PROYEK) sering kali dilewati (skipped) oleh IMAP client, atau spasi di dalamnya otomatis dipotong (menjadi 00.KNOWLEDGEBASE).
  2. Arsitektur Relasi Dua Lapis Database Zimbra:
    • Kolom tag_names (Tabel mail_item): Di shard database MariaDB Zimbra (mboxgroup<N>), setiap baris email mencatat string riwayat nama tag yang pernah menempel pada kolom tag_names. Data teks ini tetap tersimpan utuh saat email masuk ke server baru.
    • Tabel Relasi tagged_item & Index Pencarian: Webmail Zimbra menampilkan email pada folder tag berdasarkan relasi aktif antara Item ID email dan Tag ID numerik. Saat tag baru dibuat ulang di server baru, Zimbra memberikan Tag ID baru (misalnya ID 9771). Zimbra tidak otomatis menghubungkan Tag ID baru ini dengan teks string di kolom tag_names lama.

Artinya, tugas kita adalah membaca string tag dari database MariaDB lokal, lalu mengikatkannya kembali ke Tag ID resmi akun menggunakan perintah batch tagMessage.

1. Identifikasi Mailbox ID dan Database Shard MariaDB

Langkah pertama adalah mencari ID mailbox dan shard database MariaDB untuk akun yang bersangkutan (misal akun raihan.utomo@colamen.id pada server mbox2.colamen.id).

Login sebagai user zimbra di server mailbox:

# su - zimbra
# zmprov getMailboxInfo raihan.utomo@colamen.id

Contoh output yang diperoleh:

mailboxId: 274
quotaUsed: 8256192190

Di Zimbra, database di-sharding ke schema mboxgroup<N> dengan rumus N = mailbox_id % 100. Untuk mailboxId: 274, shard databasenya adalah mboxgroup74.

Kita dapat memverifikasinya langsung ke database utama zimbra:

# mysql -e "SELECT id, group_id FROM zimbra.mailbox WHERE id = 274;"

Output konfirmasi:

+-----+----------+
| id  | group_id |
+-----+----------+
| 274 |       74 |
+-----+----------+

2. Memeriksa Rekaman Metadata Tag di Database Zimbra

Mari kita pastikan bahwa jejak nama tag lama memang tersimpan di database MariaDB:

# mysql -e "SELECT DISTINCT tag_names FROM mboxgroup74.mail_item WHERE mailbox_id = 274 AND tag_names IS NOT NULL LIMIT 20;"

Contoh output yang muncul:

+---------------------------------------------------------------------------------+
| tag_names                                                                       |
+---------------------------------------------------------------------------------+
|  VENDOR                                                                         |
|  REIMBURSEMENT                                                                  |
|  FINANCE                                                                        |
|  01. DOKUMEN KONTRAK                                                            |
|  03. INVOICE                                                                    |
|  00.URGENT                                                                      |
|  04.SURATJALAN                                                                  |
|  04. SURAT JALAN                                                                |
|  CLIENT A                                                                       |
|  00.KNOWLEDGEBASE                                                               |
|  02.POCUSTOMER                                                                  |
|  02. PO CUSTOMER 00. URGENT                                                     |
+---------------------------------------------------------------------------------+

Perhatikan bahwa kolom tag_names menyimpan teks tag dengan sangat baik, termasuk variasi dengan spasi (04. SURAT JALAN), tanpa spasi (04.SURATJALAN, 00.URGENT), maupun email yang memiliki kombinasi multi-tag (02. PO CUSTOMER 00. URGENT).

Selanjutnya, cek total email pesan (hanya tipe pesan email atau type = 5):

# mysql -e "SELECT count(*) AS total_email FROM mboxgroup74.mail_item WHERE mailbox_id = 274 AND type = 5 AND tag_names IS NOT NULL;"

Kita juga bisa menguji sampel salah satu email untuk melihat kecocokan datanya:

# mysql -e "SELECT id, subject, tag_names, FROM_UNIXTIME(date) AS date FROM mboxgroup74.mail_item WHERE mailbox_id = 274 AND (tag_names LIKE '%00.KNOWLEDGEBASE%' OR tag_names LIKE '%00. KNOWLEDGE BASE%') LIMIT 5;"

Contoh output sampel:

+------+--------------------------------------------------------------------+---------------------------------+---------------------+
| id   | subject                                                            | tag_names                       | date                |
+------+--------------------------------------------------------------------+---------------------------------+---------------------+
| 3824 | Notulen Rapat Koordinasi Tim Infrastruktur                         |  00.KNOWLEDGEBASE 00.URGENT     | 2025-11-17 19:44:20 |
| 3825 | Notulen Rapat Koordinasi Tim Infrastruktur                         |  00. KNOWLEDGE BASE 00. URGENT  | 2026-09-28 11:58:10 |
| 4092 | Laporan Evaluasi Kinerja Sistem & Pemeliharaan Berkala             |  00.KNOWLEDGEBASE               | 2026-02-12 09:35:56 |
| 4115 | Panduan Operasional Prosedur Pengelolaan Backup Data               |  00.KNOWLEDGEBASE VENDOR        | 2026-02-16 11:08:13 |
| 4461 | Koordinasi Kesiapan Layanan Server Mailbox Produksi                |  00.KNOWLEDGEBASE               | 2026-04-21 14:25:52 |
+------+--------------------------------------------------------------------+---------------------------------+---------------------+

3. Menyiapkan Definisi Tag Resmi di Akun Pengguna

Sebelum perintah pemasangan tag dijalankan, pastikan seluruh nama tag resmi dan warnanya sudah dibuat di akun target di server baru. Jika belum, buat menggunakan perintah createTag:

# zmmailbox -z -m raihan.utomo@colamen.id createTag -c red "00. KNOWLEDGE BASE"
# zmmailbox -z -m raihan.utomo@colamen.id createTag -c gray "00. MEMO INTERNAL"
# zmmailbox -z -m raihan.utomo@colamen.id createTag -c red "00. URGENT"
# zmmailbox -z -m raihan.utomo@colamen.id createTag -c purple "01. PROPOSAL PROYEK"
# zmmailbox -z -m raihan.utomo@colamen.id createTag -c cyan "02. PO CUSTOMER"
# zmmailbox -z -m raihan.utomo@colamen.id createTag -c blue "03. INVOICE"

Verifikasi seluruh tag yang sudah terdaftar pada akun dengan perintah:

# zmmailbox -z -m raihan.utomo@colamen.id getAllTags

Output akan menampilkan daftar Tag ID baru, status unread, warna, dan nama tag resmi.

4. Menyiapkan Script Python Engine Pemulihan Otomatis

Untuk memproses ribuan email tanpa risiko salah pasang (collision) antara tag berpenamaan mirip (contohnya tag 04. SURAT JALAN KHUSUS vs 04. SURAT JALAN), kita perlu script yang mengurutkan pencocokan dari nama tag terpanjang ke terpendek, serta fleksibel mencocokkan format teks berspasi maupun tanpa spasi.

Buat file script Python di /tmp/restore_tags_raihan.py:

#!/usr/bin/env python3
import subprocess
import os
import re
from collections import defaultdict

# ================= KONFIGURASI =================
ACCOUNT = "raihan.utomo@colamen.id"
MAILBOX_ID = 274
GROUP_ID = 74
DRYRUN_FILE = "/tmp/batch_tag_commands_dryrun.txt"

# Daftar resmi tag yang ada pada akun
TAG_LIST = [
    "00. KNOWLEDGE BASE", "00. MEMO INTERNAL", "00. PANDUAN TEKNIS", "00. URGENT",
    "01. PROPOSAL PROYEK", "01. DOKUMEN KONTRAK", "01. LAPORAN EVALUASI",
    "02. PO CUSTOMER", "02. PURCHASE REQUEST", "03. INVOICE", "03. PAYMENT RECEIPT",
    "04. SURAT JALAN KHUSUS", "04. SURAT JALAN", "05. LAPORAN MINGGUAN",
    "05. LAPORAN BULANAN", "06. TICKETING SUPPORT", "07. KOMPLAIN KLIEN",
    "CLIENT A", "CLIENT B", "CLIENT C", "FINANCE", "HRD", "MARKETING",
    "OPERASIONAL", "REIMBURSEMENT", "VENDOR", "INTERNAL AUDIT"
]
# ===============================================

print(f"[*] Menjalankan audit database untuk akun {ACCOUNT}...")
query = (
    f"SELECT id, tag_names FROM mboxgroup{GROUP_ID}.mail_item "
    f"WHERE mailbox_id = {MAILBOX_ID} AND type = 5 AND tag_names IS NOT NULL;"
)
cmd = ["mysql", "-BNe", query]
result = subprocess.run(cmd, stdout=subprocess.PIPE, text=True, check=True)

# Urutkan nama tag dari yang terpanjang ke terpendek
sorted_tags = sorted(TAG_LIST, key=len, reverse=True)

tag_counts = defaultdict(int)
total_matched_commands = 0
unmatched_entries = []
processed_emails = 0

with open(DRYRUN_FILE, "w", encoding="utf-8") as f_out:
    for line in result.stdout.strip().split("\n"):
        if not line:
            continue
        parts = line.split("\t")
        if len(parts) < 2:
            continue
        msg_id, raw_tags = parts[0], parts[1]
        processed_emails += 1

        text_to_search = f" {raw_tags} "

        for tag in sorted_tags:
            tag_spaceless = tag.replace(" ", "")

            matched = False
            if tag.lower() in text_to_search.lower():
                matched = True
            elif tag_spaceless.lower() in text_to_search.lower():
                matched = True

            if matched:
                f_out.write(f'tagMessage {msg_id} "{tag}" 1\n')
                tag_counts[tag] += 1
                total_matched_commands += 1
                
                # Substraksi teks yang sudah cocok agar tidak trigger collision
                text_to_search = re.sub(re.escape(tag), " ", text_to_search, flags=re.IGNORECASE)
                text_to_search = re.sub(re.escape(tag_spaceless), " ", text_to_search, flags=re.IGNORECASE)

        # Cek apakah ada sisa string yang belum terpetakan
        clean_rem = text_to_search.replace("\x00", "").strip()
        if clean_rem:
            unmatched_entries.append((msg_id, raw_tags, clean_rem))

print(f"\n[+] Total Email Pesan (type = 5) Diproses : {processed_emails}")
print(f"[+] Total Perintah tagMessage yang Dibuat  : {total_matched_commands}")
print(f"[+] File Output Batch Disimpan di         : {DRYRUN_FILE}\n")

print("-" * 60)
print(f"{'NAMA TAG':<35} | {'JUMLAH EMAIL': 0:
        print(f"{tag: sisa='{rem}'")
else:
    print("\n[V] Semua tag cocok 100% tanpa ada sisa yang terlewat!")

5. Menjalankan Simulasi (Dry-Run) untuk Memvalidasi Pemetaan

Sebelum melakukan perubahan apapun pada mailbox aktif pengguna, jalankan script dalam mode simulasi:

# python3 /tmp/restore_tags_raihan.py

Contoh output statistik simulasi:

============================================================
      DRY-RUN SIMULATION RE-TAGGING: raihan.utomo@colamen.id
============================================================

[+] Total Email Pesan (type = 5) Diproses : 2467
[+] Total Perintah tagMessage yang Dibuat  : 2724
[+] File Output Batch Disimpan di         : /tmp/batch_tag_commands_dryrun.txt

------------------------------------------------------------
NAMA TAG                            | JUMLAH EMAIL   
------------------------------------------------------------
00. KNOWLEDGE BASE                  | 5 email
00. MEMO INTERNAL                   | 3 email
00. URGENT                          | 68 email
01. PROPOSAL PROYEK                 | 396 email
01. DOKUMEN KONTRAK                 | 18 email
02. PO CUSTOMER                     | 90 email
03. INVOICE                         | 197 email
04. SURAT JALAN                     | 61 email
04. SURAT JALAN KHUSUS              | 5 email
FINANCE                             | 164 email
REIMBURSEMENT                       | 14 email
VENDOR                              | 131 email
...
------------------------------------------------------------
[V] Semua tag cocok 100% tanpa ada sisa yang terlewat!

--- SAMPEL 10 BARIS PERINTAH YANG DIHASILKAN ---
  tagMessage 972 "VENDOR" 1
  tagMessage 1113 "REIMBURSEMENT" 1
  tagMessage 1655 "FINANCE" 1
  tagMessage 2091 "01. DOKUMEN KONTRAK" 1
  tagMessage 3661 "03. INVOICE" 1
------------------------------------------------------------

Dari 2.467 email pesan asli, berhasil dihasilkan tepat 2.724 instruksi tagMessage (angka instruksi lebih banyak karena ada email yang memiliki multi-tag sekaligus, seperti PO + Invoice).

6. Eksekusi Batch Re-Tagging via zmmailbox (Single JVM Session)

PENTING: Jangan mengeksekusi perintah tagMessage menggunakan perulangan (loop) baris-per-baris di bash shell (seperti while read... do zmmailbox... done). Menjalankan loop tersebut akan memicu JVM Java baru untuk setiap baris, yang akan membuat proses memakan waktu 1–2 jam dan membebani resource server.

Sebaliknya, gunakan metode Stdin Redirection ke dalam satu sesi zmmailbox:

# zmmailbox -z -m raihan.utomo@colamen.id < /tmp/batch_tag_commands_dryrun.txt > /tmp/apply_tags.log 2>&1

Dengan metode ini, seluruh 2.724 instruksi selesai dieksekusi hanya dalam waktu 30–60 detik!

Setelah proses selesai, periksa log untuk memastikan tidak ada pesan error:

# grep -i "error" /tmp/apply_tags.log | head -n 10

Jika outputnya kosong, berarti 100% perintah tag berhasil diterapkan tanpa kendala sama sekali.

7. Re-Index Mailbox dan Flush Cache

Agar Zimbra menyinkronkan tabel relasi database dengan mesin pencariannya, jalankan re-index mailbox di background:

# zmprov rim raihan.utomo@colamen.id start
status: started

Pantau progress re-index secara berkala:

# zmprov rim raihan.utomo@colamen.id status
status: running
progress: numSucceeded=398, numFailed=0, numRemaining=8046

Tunggu hingga statusnya berubah menjadi idle (biasanya hanya 1–2 menit untuk ~8.500 pesan). Setelah selesai, lakukan flush cache akun:

# zmprov fc account raihan.utomo@colamen.id

8. Pengujian dan Verifikasi Hasil

Uji pencarian tag pada CLI untuk tag bervolume besar, misalnya 01. PROPOSAL PROYEK:

# zmmailbox -z -m raihan.utomo@colamen.id search -t message -l 5 "tag:"01. PROPOSAL PROYEK""
num: 3, more: true

      Id  Type   From                  Subject                                             Date
   -----  ----   --------------------  --------------------------------------------------  --------------
1. 11195  mess   Raihan                Proposal Implementasi Sistem Cloud - PT Maju        10/12/26 09:05
2. 11086  mess   Raihan                Re: Penawaran Lisensi & Pemeliharaan Server         10/09/26 14:54
3. 11010  mess   Raihan                Proposal Pengadaan Lisensi Tambahan                 10/09/26 10:33

Catatan Mengenai Kolom Unread pada getAllTags: Jika rekan-rekan mengecek zmmailbox getAllTags dan mendapati kolom kedua bernilai 0, tidak perlu khawatir. Kolom tersebut merepresentasikan jumlah email yang belum dibaca (Unread). Karena email-email lama yang dimigrasikan statusnya sudah dibaca (seen), maka nilainya wajar 0. Total email akan tampil lengkap saat folder tag diklik di Webmail.

Minta user untuk me-refresh Webmail mereka (tekan Ctrl + F5 atau Cmd + Shift + R). Seluruh folder tag di panel kiri akan langsung memuat email lama dengan rapi dan akurat!

Kesimpulan

Dengan memanfaatkan metadata tag_names yang masih tersimpan di MariaDB shard Zimbra, kita dapat memetakan dan menyuntikkan kembali seluruh relasi tag email yang hilang pasca migrasi imapsync secara cepat, presisi, dan aman tanpa perlu mengulang proses migrasi.

Apabila rekan-rekan mengalami kesulitan atau memiliki kebutuhan terkait instalasi, pemeliharaan, migrasi, maupun pemulihan data (recovery) Zimbra Mail Server, Excellent menyediakan layanan implementasi dan Excellent Managed Services (EMS) untuk Zimbra. Bagi rekan-rekan yang berminat untuk jasa layanan tersebut bisa langsung kontak & tanya-tanya ke email sales@excellent.co.id.

Leave a Reply

Your email address will not be published. Required fields are marked *