Penulisan data time-series berbeda dari penulisan data operasional karena didominasi penambahan di ujung termutakhir, dengan dua komplikasi yang sering muncul di lapangan, yaitu kedatangan terlambat dan data yang tidak terurut. Topik ini membedah tiga pola penulisan, yaitu ingestion searah, late arrival, dan out-of-order, lalu menunjukkan dampaknya pada urutan baca dan kebijakan penerimaan data. Setelah membaca topik ini, pilihan strategi ingestion menjadi lebih jelas, termasuk kapan batch, upsert, dan backfill layak digunakan.
Penambahan di ujung termutakhir#
Pola penulisan time-series yang paling dasar adalah penambahan di ujung termutakhir (append-mostly). Setiap titik baru disisipkan dengan cap waktu yang lebih besar dari titik sebelumnya, sehingga baris tersusun menurut kronologi tanpa usaha pengurutan eksplisit. Sifat ini membuat struktur penyimpanan dapat dioptimalkan untuk aliran searah, yaitu halaman baru dialokasikan di akhir tabel, indeks B-tree tumbuh ke arah daun termutakhir, dan partisi waktu aktif hanya satu atau beberapa. Karena itu, laju ingest tinggi menjadi tujuan yang dapat dicapai, bukan pertanyaan teoretis.
Konsekuensi dari pola ini adalah minimnya pembaruan. Baris lama hampir tidak pernah diubah, sebab nilai pengukuran historis dianggap final. Ketika koreksi memang perlu, pola yang digunakan adalah penambahan baris baru dengan cap waktu baru, bukan pembaruan baris lama. Sifat ini disebut immutability historis, dan mesin time-series memanfaatkannya untuk mengompresi chunk lama ke format kolumnar yang tidak dirancang untuk pembaruan acak. Asalkan urutan kedatangan mengikuti urutan kronologi, jalur penulisan tetap berjalan lurus ke depan.
Kedatangan terlambat#
Kenyataan lapangan tidak selalu mengikuti pola searah yang rapi. Pada sistem IoT, sensor dapat kehilangan koneksi selama beberapa menit hingga beberapa jam, lalu mengirim batch titik yang sudah melalui cap waktunya. Pada sistem pemantauan, keterlambatan jaringan dapat membuat titik dengan cap waktu lampau tiba setelah titik yang lebih baru. Fenomena ini disebut late arrival, dan frekuensinya bergantung pada karakter sumber data.
Late arrival memaksa skema untuk menerima penambahan dengan cap waktu yang lebih kecil dari cap waktu termutakhir di penyimpanan. Mesin time-series umumnya menangani hal ini dengan upsert atau dengan menempatkan titik terlambat ke chunk yang sesuai berdasarkan cap waktunya, bukan berdasarkan urutan kedatangan. Tanpa mekanisme ini, data terlambat akan ditolak atau tertumpuk di chunk termutakhir, sehingga partisi waktu kehilangan makna. Perbedaan antara urutan kedatangan dan urutan kronologi menjadi fakta dasar yang harus diasumsikan oleh setiap query.
Data tak berurutan#
Kasus yang lebih sulit terjadi apabila sumber data bersifat paralel. Beberapa sensor yang melaporkan ke agregator yang sama dapat mengirim batch dengan cap waktu yang tumpang tindih, dan agregator tidak selalu mengurutkan ulang sebelum menulis. Hasilnya adalah data yang masuk tidak terurut menurut cap waktu, meskipun masing-masing titik sudah benar secara semantik. Kondisi ini disebut out-of-order.
Out-of-order bukan kesalahan yang dapat dihindari sepenuhnya pada sistem terdistribusi. Yang dapat dilakukan adalah memilih tingkat toleransi, yaitu berapa lama jendela penerimaan data terlambat dibiarkan terbuka, dan apa yang terjadi pada titik yang tiba setelah jendela itu ditutup. Banyak mesin time-series menyediakan parameter semacam max_past atau setara, yang menentukan seberapa lama ke belakang sebuah titik masih diterima. Titik yang lebih lama dari ambang ini ditolak, sebab memasukkannya akan memicu pembaruan chunk yang sudah dikompresi atau diolah.
Simulasi ingestion dengan late arrival#
Untuk memahami dampak late arrival secara konkret, skrip Python berikut membangkitkan aliran telemetri ideal selama 30 menit, lalu menyisipkan 5 titik yang cap waktunya berada di menit ke-5 hingga ke-9, tetapi baru ditambahkan setelah titik termutakhir. Skrip menggunakan pandas untuk membangun deret dan numpy untuk menambahkan noise, kemudian mencetak urutan kedatangan dan urutan kronologi setelah sort_values. Hasilnya memperlihatkan bahwa urutan kedatangan bukan urutan kronologi, dan urutan kronologi adalah yang digunakan oleh query.
"""Simulasi ingestion telemetri dengan kedatangan terlambat.
Skrip membangkitkan 30 titik dengan interval satu menit sebagai aliran ideal,
lalu menyisipkan 5 titik yang cap waktunya berada di tengah deret tetapi baru
ditambahkan setelah titik termutakhir. Hasilnya membandingkan urutan kedatangan
dengan urutan kronologi setelah pengurutan ulang, agar perbedaan keduanya
terlihat secara konkret.
"""
from __future__ import annotations
from datetime import datetime, timedelta, timezone
import numpy as np
import pandas as pd
AWAL = datetime(2026, 1, 1, 12, 0, tzinfo=timezone.utc)
JUMLAH_ALIRAN = 30
JUMLAH_TERLAMBAT = 5
def bangun_aliran() -> pd.DataFrame:
cap_waktu = [AWAL + timedelta(minutes=i) for i in range(JUMLAH_ALIRAN)]
rng = np.random.default_rng(7)
suhu = 22.0 + np.sin(np.arange(JUMLAH_ALIRAN) / 5.0) + rng.normal(0, 0.3, JUMLAH_ALIRAN)
return pd.DataFrame({
"ts": pd.to_datetime(cap_waktu),
"sensor_id": "sensor-01",
"suhu_c": np.round(suhu, 2),
})
def sisipkan_terlambat(df: pd.DataFrame) -> pd.DataFrame:
rng = np.random.default_rng(11)
awal_terlambat = AWAL + timedelta(minutes=5)
cap_terlambat = [awal_terlambat + timedelta(minutes=i) for i in range(JUMLAH_TERLAMBAT)]
suhu_terlambat = 23.0 + rng.normal(0, 0.3, JUMLAH_TERLAMBAT)
tambahan = pd.DataFrame({
"ts": pd.to_datetime(cap_terlambat),
"sensor_id": "sensor-01",
"suhu_c": np.round(suhu_terlambat, 2),
})
return pd.concat([df, tambahan], ignore_index=True)
def main() -> None:
df_urut = bangun_aliran()
df_campur = sisipkan_terlambat(df_urut)
print("urutan kedatangan (5 baris pertama dan 5 terakhir):")
print(df_campur.head(5).to_string(index=False))
print("...")
print(df_campur.tail(5).to_string(index=False))
print()
df_terurut = df_campur.sort_values("ts").reset_index(drop=True)
print("urutan kronologi (5 baris pertama setelah sort_values):")
print(df_terurut.head(5).to_string(index=False))
print()
print("jumlah baris:", len(df_campur))
print("rentang waktu:", df_campur["ts"].min(), "->", df_campur["ts"].max())
print("jumlah baris terlambat:", JUMLAH_TERLAMBAT)
if __name__ == "__main__":
main()urutan kedatangan (5 baris pertama dan 5 terakhir):
ts sensor_id suhu_c
2026-01-01 12:00:00+00:00 sensor-01 22.00
2026-01-01 12:01:00+00:00 sensor-01 22.29
2026-01-01 12:02:00+00:00 sensor-01 22.31
2026-01-01 12:03:00+00:00 sensor-01 22.30
2026-01-01 12:04:00+00:00 sensor-01 22.58
...
ts sensor_id suhu_c
2026-01-01 12:05:00+00:00 sensor-01 23.01
2026-01-01 12:06:00+00:00 sensor-01 23.41
2026-01-01 12:07:00+00:00 sensor-01 23.37
2026-01-01 12:08:00+00:00 sensor-01 22.85
2026-01-01 12:09:00+00:00 sensor-01 22.91
urutan kronologi (5 baris pertama setelah sort_values):
ts sensor_id suhu_c
2026-01-01 12:00:00+00:00 sensor-01 22.00
2026-01-01 12:01:00+00:00 sensor-01 22.29
2026-01-01 12:02:00+00:00 sensor-01 22.31
2026-01-01 12:03:00+00:00 sensor-01 22.30
2026-01-01 12:04:00+00:00 sensor-01 22.58
jumlah baris: 35
rentang waktu: 2026-01-01 12:00:00+00:00 -> 2026-01-01 12:29:00+00:00
jumlah baris terlambat: 5Pada keluaran di atas, baris terakhir di urutan kedatangan memiliki cap waktu 12:09, sedangkan baris di atasnya pada batch asli memiliki cap waktu hingga 12:29. Hal itu membuktikan bahwa urutan kedatangan melompat mundur ke 12:05 hingga 12:09 setelah batch asli selesai pada 12:29. Setelah sort_values("ts"), urutan kronologi menjadi monotok naik, dan baris terlambat menempati posisi yang benar di tengah deret. Inilah alasan query time-series selalu mengandalkan kolom cap waktu, bukan urutan baris fisik, sebab urutan fisik tidak mencerminkan kronologi ketika late arrival terjadi.
Kesalahan umum#
- Mengasumsikan urutan kedatangan sama dengan urutan kronologi. Pada sistem dengan late arrival, query yang menggunakan urutan baris alih-alih kolom
tsakan menghasilkan jawaban yang salah. - Menerima data terlambat tanpa batas waktu. Membuka jendela penerimaan terlalu lebar membuat chunk lama tetap dapat diubah, sehingga kompresi dan retensi tertunda.
- Menggunakan upsert untuk setiap penulisan. Upsert memerlukan pencarian baris yang sudah ada, dan biayanya jauh lebih besar daripada penambahan biasa. Upsert sebaiknya digunakan hanya bila duplikasi memang mungkin terjadi.
Ringkasan#
- Pola penulisan time-series didominasi penambahan di ujung termutakhir, dengan sifat immutability historis pada baris lama.
- Late arrival adalah kenyataan pada sistem IoT dan pemantauan, dan ditangani dengan upsert atau penempatan ulang ke chunk yang sesuai berdasarkan cap waktu.
- Out-of-order bersumber dari paralelisme pada sumber data, dan ditangani dengan jendela penerimaan yang eksplisit.
- Urutan kedatangan dan urutan kronologi adalah dua hal yang berbeda, dan query harus selalu mengandalkan kolom cap waktu.
Referensi#
- Hypertables — Timescale — Penjelasan resmi tentang hypertable sebagai abstraksi tabel yang dipartisi otomatis berdasarkan waktu, termasuk penanganan data tidak terurut.
- Line protocol — InfluxDB 3 — Sintaks resmi line protocol beserta aturan cap waktu opsional dan konsekuensi penulisan tanpa cap waktu.
- INSERT — PostgreSQL
— Dokumentasi resmi perintah
INSERT, termasuk klausaON CONFLICTyang menjadi dasar mekanisme upsert.