fix: dzienny ingest filmow nie przepada o 05:00, apka nie staje na bledzie keystore

- scheduler: misfire_grace_time 1 h zamiast domyslnej 1 s. O 05:00 UTC startuje kilkanascie
  zadan naraz, movie-ingest dostawal watek 1,1 s po czasie i APScheduler go pomijal, wiec
  filmy nie przychodzily od 17.09 (watchdog, Sentry).
- getDeviceId: blad SecureStore ("Could not encrypt the value", Samsung A7, Android 10)
  wywalal kazde zapytanie do API. Teraz id zostaje w pamieci do konca sesji. Wejdzie z
  najblizsza paczka OTA.
- latka expo-modules-core 2.2.3, JNIDeallocator: watek sprzatajacy blokowal sie na sobie,
  nie na deallocatorze, stad ConcurrentModificationException przy przeladowaniu JS
  (Sentry GOON-48). Wejdzie z najblizszym APK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
goon-foss 2026-09-19 22:01:45 +02:00
parent 73853eb9ed
commit ca2a6a1547
3 changed files with 52 additions and 3 deletions

View file

@ -525,7 +525,12 @@ def build_scheduler(cfg: dict[str, Any]) -> BlockingScheduler:
performer_continuous_seconds: int | None performer_continuous_seconds: int | None
performer_continuous_refresh_days: int performer_continuous_refresh_days: int
""" """
sched = BlockingScheduler(timezone="UTC") # misfire_grace_time: domyslnie 1 s. O 05:00 UTC (wspolny anchor) startuje kilkanascie
# zadan naraz i dzienny movie-ingest dostawal watek 1,1 s po czasie, wiec APScheduler go
# POMIJAL ("was missed by 0:00:01"), dzien w dzien. Filmy staly 4-8 dni, zanim watchdog
# to pokazal (2026-09-19). Godzina zapasu; coalesce i max_instances=1 i tak zbijaja
# zaleglosci w jedno uruchomienie.
sched = BlockingScheduler(timezone="UTC", job_defaults={"misfire_grace_time": 3600})
sched.add_job( sched.add_job(
_job_site_icons, _job_site_icons,

View file

@ -0,0 +1,31 @@
diff --git a/node_modules/expo-modules-core/android/src/main/java/expo/modules/kotlin/jni/JNIDeallocator.kt b/node_modules/expo-modules-core/android/src/main/java/expo/modules/kotlin/jni/JNIDeallocator.kt
--- a/node_modules/expo-modules-core/android/src/main/java/expo/modules/kotlin/jni/JNIDeallocator.kt
+++ b/node_modules/expo-modules-core/android/src/main/java/expo/modules/kotlin/jni/JNIDeallocator.kt
@@ -34,7 +34,10 @@ class JNIDeallocator(shouldCreateDestructorThread: Boolean = true) : AutoCloseab
// Referent of PhantomReference were garbage collected so we can remove it from our registry.
// Note that we don't have to call `deallocate` method - it was called [com.facebook.jni.HybridData].
val current = referenceQueue.remove()
- synchronized(this) {
+ // Goon: `this` w `object : Thread` to watek, nie deallocator. Blokada na watku
+ // nic nie chronila i remove szedl rownolegle z iteracja w deallocate(), stad
+ // ConcurrentModificationException przy przeladowaniu JS (Sentry GOON-48).
+ synchronized(this@JNIDeallocator) {
destructorMap.remove(current)
}
} catch (e: InterruptedException) {
@@ -65,10 +68,13 @@ class JNIDeallocator(shouldCreateDestructorThread: Boolean = true) : AutoCloseab
* Deallocates valid references and clears the internal registry.
*/
internal fun deallocate() = synchronized(this) {
- destructorMap.values.forEach {
+ // Goon: kopia przed wolaniem deallocate(), ktore moze wrocic do tej mapy z tego
+ // samego watku (blokada jest reentrant, wiec by nie zatrzymala), GOON-48.
+ val refs = destructorMap.values.toList()
+ destructorMap.clear()
+ refs.forEach {
it.get()?.deallocate()
}
- destructorMap.clear()
destructorThread?.interrupt()
}

View file

@ -18,10 +18,23 @@ function _genDeviceId(): string {
export async function getDeviceId(): Promise<string> { export async function getDeviceId(): Promise<string> {
if (_deviceIdCache) return _deviceIdCache; if (_deviceIdCache) return _deviceIdCache;
let id = await SecureStore.getItemAsync(DEVICE_KEY); // Keystore potrafi odmowic ("Could not encrypt the value for key 'goon.device_id'",
// Samsung A7 z Androidem 10, Sentry 2026-09-19). Kazde zapytanie do API czeka na to id,
// wiec wyjatek stad wylaczal cala apke. Teraz id zostaje w pamieci do konca sesji; bez
// zapisu po restarcie powstanie nowe, ale apka dziala.
let id: string | null = null;
try {
id = await SecureStore.getItemAsync(DEVICE_KEY);
} catch {
id = null;
}
if (!id) { if (!id) {
id = _genDeviceId(); id = _genDeviceId();
await SecureStore.setItemAsync(DEVICE_KEY, id); try {
await SecureStore.setItemAsync(DEVICE_KEY, id);
} catch {
// zostaje tylko w _deviceIdCache
}
} }
_deviceIdCache = id; _deviceIdCache = id;
return id; return id;