mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-11 16:57:39 +00:00
perf(tor): stop wiping Arti's consensus cache on every start
TorService.start() called clearArtiCache() on every cold start (and every reset, since reset flips initialized back to false), deleting the consensus/microdescriptor cache and forcing a full re-download on the next bootstrap. Measured on device, that turned a ~7.7s warm bootstrap into ~24.2s — a ~3.2x slowdown paid on every launch. The wipe was an early attempt at what we later understood to be the wedged- guard problem (now handled by noUsableGuards()/clearAllArtiData()). It never actually helped: guards live in state/, not cache/, so wiping the cache can't fix a stale guard sample; and Arti already validates consensus freshness and refetches whatever has expired, so there is no stale-consensus risk to guard against here. The reset/clean-state self-heal paths still call clearAllArtiData() for genuine corruption recovery. Drop clearArtiCache() entirely and preserve the cache for warm bootstraps. Also add bootstrap-timing instrumentation: the "SOCKS proxy active" log now reports elapsed bootstrap ms, and a cache-size log line correlates cache state with bootstrap time. Verified on device: warm bootstrap 7,670ms vs 24,200ms cold, with no regression — a .onion relay and 200 clearnet relays connected over Tor, 0 pre-ready doomed dials, guards healthy (59/60 usable). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
40921b80d2
commit
4f668f6175
@@ -54,6 +54,13 @@ class TorService(
|
||||
private val initialized = AtomicBoolean(false)
|
||||
private val proxyRunning = AtomicBoolean(false)
|
||||
|
||||
/**
|
||||
* Wall-clock at which the current `initialize()` started, so the bootstrap log can
|
||||
* report the elapsed time to "Sufficiently bootstrapped". -1 when no init is in flight.
|
||||
* Diagnostic only.
|
||||
*/
|
||||
@Volatile private var bootstrapStartedAtMs: Long = -1L
|
||||
|
||||
/**
|
||||
* Serializes every native lifecycle transition ([start], [stop], [reset],
|
||||
* [resetWithCleanState]). [ArtiNative] is a process-global singleton over a
|
||||
@@ -72,18 +79,11 @@ class TorService(
|
||||
|
||||
private fun artiDataDir() = File(context.filesDir, "arti")
|
||||
|
||||
/**
|
||||
* Clears the Arti cache directory (consensus, relay descriptors) while
|
||||
* preserving the state directory (guard selection). This forces a fresh
|
||||
* consensus download on the next bootstrap, preventing stale cached data
|
||||
* from causing circuit failures.
|
||||
*/
|
||||
private fun clearArtiCache() {
|
||||
/** Diagnostic: total bytes of the consensus/descriptor cache, to correlate with bootstrap time. */
|
||||
private fun cacheSizeBytes(): Long {
|
||||
val cacheDir = File(artiDataDir(), "cache")
|
||||
if (cacheDir.exists()) {
|
||||
cacheDir.deleteRecursively()
|
||||
Log.d("TorService") { "Cleared Arti cache directory" }
|
||||
}
|
||||
if (!cacheDir.exists()) return 0
|
||||
return cacheDir.walkBottomUp().filter { it.isFile }.sumOf { it.length() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,15 +196,23 @@ class TorService(
|
||||
// not resurrect a stale Active status.
|
||||
if (proxyRunning.get()) {
|
||||
_status.value = TorServiceStatus.Active(socksPort)
|
||||
Log.d("TorService") { "Arti SOCKS proxy active on port $socksPort" }
|
||||
val startedAt = bootstrapStartedAtMs
|
||||
val elapsed = if (startedAt > 0) System.currentTimeMillis() - startedAt else -1
|
||||
Log.d("TorService") { "Arti SOCKS proxy active on port $socksPort (bootstrap took ${elapsed}ms)" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear cached consensus/descriptors so Arti bootstraps with
|
||||
// fresh network data, preventing stale guards/circuits.
|
||||
clearArtiCache()
|
||||
// Preserve the consensus/descriptor cache across cold starts. We used to
|
||||
// wipe it on every launch, which forced a full microdescriptor consensus
|
||||
// re-download and turned a ~7s warm bootstrap into ~24s. That wipe was an
|
||||
// early attempt at what turned out to be the wedged-guard problem (now
|
||||
// handled by [noUsableGuards]); it never actually helped, since guards live
|
||||
// in state/ — not cache/ — and Arti already validates consensus freshness
|
||||
// and refetches whatever has expired. The reset/clean-state paths still call
|
||||
// clearAllArtiData() for genuine corruption recovery.
|
||||
Log.d("TorService") { "Preserving Arti cache for warm bootstrap (cache size: ${cacheSizeBytes()} bytes)" }
|
||||
|
||||
// Self-heal the wedged guard sample (see [noUsableGuards]): if
|
||||
// the persisted sample has no usable guard left, Arti can
|
||||
@@ -219,6 +227,7 @@ class TorService(
|
||||
val dataDir = artiDataDir().absolutePath
|
||||
Log.d("TorService") { "Initializing Arti with data dir: $dataDir" }
|
||||
|
||||
bootstrapStartedAtMs = System.currentTimeMillis()
|
||||
var initResult = ArtiNative.initialize(dataDir)
|
||||
if (initResult != 0) {
|
||||
Log.e("TorService") { "Failed to initialize Arti: error $initResult, clearing data and retrying" }
|
||||
|
||||
Reference in New Issue
Block a user