diff --git a/app/scheduler/jobs.py b/app/scheduler/jobs.py index 6b6cd2b..0040aed 100644 --- a/app/scheduler/jobs.py +++ b/app/scheduler/jobs.py @@ -525,7 +525,12 @@ def build_scheduler(cfg: dict[str, Any]) -> BlockingScheduler: performer_continuous_seconds: int | None 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( _job_site_icons, diff --git a/mobile/patches/expo-modules-core+2.2.3.patch b/mobile/patches/expo-modules-core+2.2.3.patch new file mode 100644 index 0000000..47ea93b --- /dev/null +++ b/mobile/patches/expo-modules-core+2.2.3.patch @@ -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() + } + diff --git a/mobile/src/storage.ts b/mobile/src/storage.ts index 14cd3ff..95eacc5 100644 --- a/mobile/src/storage.ts +++ b/mobile/src/storage.ts @@ -18,10 +18,23 @@ function _genDeviceId(): string { export async function getDeviceId(): Promise { 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) { id = _genDeviceId(); - await SecureStore.setItemAsync(DEVICE_KEY, id); + try { + await SecureStore.setItemAsync(DEVICE_KEY, id); + } catch { + // zostaje tylko w _deviceIdCache + } } _deviceIdCache = id; return id;