Anatomi Crash Loop Pasca-Deployment
Rilis baru pada aplikasi React Native—baik via store update maupun over-the-air (OTA) update—rentan memicu insiden crash loop. Masalah ini umumnya berakar dari migrasi skema database lokal yang tidak kompatibel, korupsi state persistensi (misalnya Redux Persist atau MMKV), deserialisasi data payload baru yang menghasilkan null pointer dereference, atau native module linkage failure saat cold start.
Ketika aplikasi mengalami fatal crash sebelum root component selesai dirender atau sebelum ErrorBoundary terpasang, sistem operasi (Android/iOS) akan menutup aplikasi secara paksa. Pengguna yang membuka kembali aplikasi akan terjebak dalam loop startup crash yang identik. Tanpa mekanisme pemulihan otomatis, satu-satunya opsi bagi pengguna adalah melakukan uninstall dan kehilangan seluruh data lokal yang belum tersinkronisasi.
Prinsip Kerja Safe Mode Boot dan Crash Counter
Pola Safe Mode Boot mencegah crash berulang dengan mencatat siklus hidup inisialisasi aplikasi ke dalam penyimpanan sinkron berkecepatan tinggi sebelum runtime kompleks dijalankan. Alur deteksi bekerja dengan aturan berikut:
- Boot Sequence Increment: Di baris pertama eksekusi runtime (sebelum tree dependensi/state diinisialisasi), aplikasi menaikkan counter crash (
boot_crash_count) di penyimpanan lokal sinkron. - Safe Threshold Evaluation: Jika nilai
boot_crash_countmelampaui batas ambang batas (misalnya 2 atau 3 kali berturut-turut), runtime mengalihkan mounting aplikasi ke antarmuka darurat (Safe Mode Screen) dan membatalkan inisialisasi state utama. - Boot Success Reset: Jika aplikasi berhasil melewati fase inisialisasi, memuat root UI, dan bertahan stabil selama jendela waktu tertentu (misalnya 5 detik), timer menandai boot sukses dan mereset
boot_crash_countkembali ke0.
Penggunaan asynchronous storage seperti standard AsyncStorage untuk pencatatan counter memiliki risiko tinggi mengalami race condition atau gagal tersimpan jika crash terjadi sebelum I/O thread selesai mengeksekusi penulisan disk. Gunakan engine penyimpanan sinkron berbasis C++ atau JSI seperti MMKV.Implementasi Crash Loop Detection di Root Runtime
Simpan state counter menggunakan penyimpanan sinkron pada entry file aplikasi (index.js atau modul inisialisasi sebelum AppRegistry.registerComponent).
// SafeBootManager.ts
import { MMKV } from 'react-native-mmkv';
const storage = new MMKV({ id: 'boot-recovery-storage' });
const CRASH_COUNTER_KEY = 'sys_boot_consecutive_crashes';
const CRASH_THRESHOLD = 3;
const STABILIZATION_DELAY_MS = 5000;
export const SafeBootManager = {
getCrashCount(): number {
return storage.getNumber(CRASH_COUNTER_KEY) ?? 0;
},
isSafeModeTriggered(): boolean {
return this.getCrashCount() >= CRASH_THRESHOLD;
},
recordBootAttempt(): void {
const current = this.getCrashCount();
storage.set(CRASH_COUNTER_KEY, current + 1);
},
markBootSuccess(): void {
storage.set(CRASH_COUNTER_KEY, 0);
},
armStabilizationTimer(): NodeJS.Timeout {
return setTimeout(() => {
this.markBootSuccess();
}, STABILIZATION_DELAY_MS);
},
forceClearCounter(): void {
storage.set(CRASH_COUNTER_KEY, 0);
}
};
Pasang modul evaluasi pada komponen root terluar aplikasi:
// App.tsx
import React, { useEffect, useState } from 'react';
import { SafeBootManager } from './SafeBootManager';
import { SafeModeFallback } from './SafeModeFallback';
import { MainAppNavigator } from './navigation/MainAppNavigator';
import { ComplexStateProviders } from './providers/ComplexStateProviders';
// Catat boot attempt seketika saat bundle dimuat
SafeBootManager.recordBootAttempt();
export default function App() {
const [isSafeMode] = useState<boolean>(() => SafeBootManager.isSafeModeTriggered());
useEffect(() => {
if (!isSafeMode) {
const timer = SafeBootManager.armStabilizationTimer();
return () => clearTimeout(timer);
}
}, [isSafeMode]);
if (isSafeMode) {
return <SafeModeFallback />;
}
return (
<ComplexStateProviders>
<MainAppNavigator />
</ComplexStateProviders>
);
}
Fallback UI Minimalis dan Purge Storage
Komponen SafeModeFallback harus sepenuhnya independen dari Redux, mobx, navigation libraries, atau modular providers lain yang berpotensi menjadi sumber crash. Komponen ini menyediakan aksi pembersihan lokal terisolasi.
// SafeModeFallback.tsx
import React, { useState } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator } from 'react-native';
import { MMKV } from 'react-native-mmkv';
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as Sentry from '@sentry/react-native';
import { SafeBootManager } from './SafeBootManager';
export const SafeModeFallback: React.FC = () => {
const [isRecovering, setIsRecovering] = useState(false);
const handlePurgeAndRestart = async () => {
setIsRecovering(true);
try {
// 1. Kirim telemetri intervensi manual
Sentry.captureMessage('SAFE_MODE_DATA_PURGED', {
level: 'fatal',
extra: { crashCount: SafeBootManager.getCrashCount() }
});
await Sentry.flush(2000);
// 2. Isolasi pembersihan cache dan corrupted local database
await AsyncStorage.clear();
// Hapus seluruh MMKV instance selain recovery storage
const appStorage = new MMKV({ id: 'app-main-storage' });
appStorage.clearAll();
// 3. Reset counter safe mode
SafeBootManager.forceClearCounter();
// 4. Force reload via native reload atau instruksikan pengguna restart
// Alternatif: trigger fast-refresh reload runtime jika modul native tersedia
} catch (err) {
Sentry.captureException(err);
} finally {
setIsRecovering(false);
}
};
return (
<View style={styles.container}>
<Text style={styles.title}>Mode Pemulihan Aktif</Text>
<Text style={styles.body}>
Aplikasi mendeteksi kegagalan sistem berulang saat proses startup. Data lokal cache Anda mungkin mengalami kerusakan akibat proses sinkronisasi rilis terbaru.
</Text>
{isRecovering ? (
<ActivityIndicator size="large" color="#D32F2F" />
) : (
<TouchableOpacity style={styles.button} onPress={handlePurgeAndRestart}>
<Text style={styles.buttonText}>Bersihkan Cache & Reset Data Lokal</Text>
</TouchableOpacity>
)}
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 24, backgroundColor: '#FAFAFA' },
title: { fontSize: 20, fontWeight: 'bold', color: '#212121', marginBottom: 12 },
body: { fontSize: 14, color: '#616161', textAlign: 'center', marginBottom: 24, lineHeight: 20 },
button: { backgroundColor: '#D32F2F', paddingVertical: 12, paddingHorizontal: 24, borderRadius: 8 },
buttonText: { color: '#FFF', fontWeight: 'bold' }
});
Integrasi Observability dan Telemetri Insiden
Saat Safe Mode aktif, crash loop terhenti dan aplikasi tidak lagi melontarkan unhandled exception ke native layer. Konsekuensinya, dashboard crash reporting standar (seperti Sentry, Crashlytics, atau Datadog) tidak akan merekam crash lanjutan jika event tidak di-dispatch secara manual.
- Pengiriman Telemetri Eksplisit: Segera setelah Safe Mode terdeteksi, kirimkan event telemetri khusus yang melampirkan metadata versi aplikasi, build number, total kegagalan beruntun, serta sisa memori perangkat.
- Blocking Flush: Eksekusi API non-blocking logging seringkali terhenti di tengah jalan karena inisialisasi lifecycle native belum lengkap. Panggil
Sentry.flush(timeout)guna memastikan payload telemetri benar-benar terkirim lewat network socket sebelum proses purger atau reload dipicu. - Breadcrumbs Preservation: Jangan membersihkan direktori log telemetri lokal sebelum log crash awal berhasil diproyeksikan ke APM server.
Strategi Pencegahan dan Template Postmortem
Mitigasi Preventif Rilis
- Schema Version Guard: Selalu terapkan validasi skema (misal menggunakan library Zod atau runtime assert) pada setiap deserialisasi data persistensi lokal. Lakukan migrasi dengan strategi fallback-to-default bila payload korup.
- Staged Rollouts & Cold Boot Automation: Uji cold-start aplikasi menggunakan snapshot database versi lama yang di-upgrade ke bundle baru dalam pipeline CI/CD sebelum merilis update 100% ke publik.
Template Postmortem Insiden Crash Loop
# Incident Postmortem: [No-Tiket] - Crash Loop Pasca Deployment vX.Y.Z
## Ringkasan Eksekutif
- Dampak: XX% pengguna pada update vX.Y.Z mengalami startup loop crash.
- Waktu Deteksi: DD-MM-YYYY HH:mm WIB
- Waktu Resolusi: DD-MM-YYYY HH:mm WIB
- Status Safe Mode: XX perangkat teridentifikasi masuk Safe Mode dan terselamatkan.
## Timeline Insiden
- [HH:mm] - Rilis bundle vX.Y.Z diluncurkan via OTA / App Store.
- [HH:mm] - Lonjakan Crash-Free Session turun drastis di bawah target SLA (< 99.5%).
- [HH:mm] - Notifikasi threshold telemetri SAFE_MODE_TRIGGERED menyala di APM.
- [HH:mm] - Rollback target release dieksekusi.
## Root Cause Analysis (RCA)
Identifikasi penyebab runtutan crash. (Contoh: Penambahan field non-nullable pada skema SQLite
tanpa adanya script migrasi default value, memicu SQLiteConstraintException pada init repo).
## Tindakan Perbaikan (Action Items)
1. Tambahkan fallback schema error handling pada module database. (Owner: @dev, Status: TODO)
2. Turunkan threshold deteksi Safe Mode dari 3 kali menjadi 2 kali. (Owner: @dev, Status: TODO)
3. Terapkan automated upgrade test dari versi N-1 ke N di CI pipeline. (Owner: @qa, Status: TODO)
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!