Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Как я несколько дней искал ошибку в многопроцес...

Как я несколько дней искал ошибку в многопроцессном приложении

Вы можете подумать, что вас никогда это не коснется: ведь в вашем приложении всего один процесс и никаких проблем быть не может в принципе. Однако новые процессы могут появится неявно. Например, при подключении сторонних библиотек.

В докладе мы посмотрим как инициализируются многопроцессные приложения, на что стоит обращать внимание, а также в очередной раз вспомним, что SQLite не поддерживает полноценный конкурентный доступ. Конечно же доклад был бы неполноценным без решения, позволяющего избегать подобных проблем. И такое решение будет представлено.

Avatar for Daniil Popov

Daniil Popov

April 27, 2020

Other Decks in Programming

Transcript

  1. Немного обо мне ADVM #11 2 • Ведущий инженер Авито

    • Работаю над инфраструктурой Android-проекта • Занимаюсь Android разработкой с 2012 года
  2. ADVM #11 3 Ни строчки нашего кода в стэктрейсе Что

    имеем на входе? 1 2 3 Крэш приложения в случайный момент времени Не воспроизводится локально
  3. ADVM #11 4 Fatal Exception: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code

    5) at android.database.sqlite.SQLiteConnection.nativeExecute(SQLiteConnection.java) at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:553) at android.database.sqlite.SQLiteSession.beginTransactionUnchecked(SQLiteSession.java:323) at android.database.sqlite.SQLiteSession.beginTransaction(SQLiteSession.java:298) at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:505) at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:416) at androidx.sqlite.db.framework.FrameworkSQLiteDatabase.beginTransaction(FrameworkSQLiteDatabase.java:69) at androidx.room.RoomDatabase.beginTransaction(RoomDatabase.java:354) at androidx.work.impl.WorkerWrapper.runWorker(WorkerWrapper.java:135) at androidx.work.impl.WorkerWrapper.run(WorkerWrapper.java:127) at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) at java.lang.Thread.run(Thread.java:818)
  4. ADVM #11 4 Fatal Exception: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code

    5) at android.database.sqlite.SQLiteConnection.nativeExecute(SQLiteConnection.java) at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:553) at android.database.sqlite.SQLiteSession.beginTransactionUnchecked(SQLiteSession.java:323) at android.database.sqlite.SQLiteSession.beginTransaction(SQLiteSession.java:298) at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:505) at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:416) at androidx.sqlite.db.framework.FrameworkSQLiteDatabase.beginTransaction(FrameworkSQLiteDatabase.java:69) at androidx.room.RoomDatabase.beginTransaction(RoomDatabase.java:354) at androidx.work.impl.WorkerWrapper.runWorker(WorkerWrapper.java:135) at androidx.work.impl.WorkerWrapper.run(WorkerWrapper.java:127) at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) at java.lang.Thread.run(Thread.java:818)
  5. ADVM #11 9 Fatal Exception: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code

    5) at android.database.sqlite.SQLiteConnection.nativeExecute(SQLiteConnection.java) at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:553) at android.database.sqlite.SQLiteSession.beginTransactionUnchecked(SQLiteSession.java:323) at android.database.sqlite.SQLiteSession.beginTransaction(SQLiteSession.java:298) at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:505) at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:416) at androidx.sqlite.db.framework.FrameworkSQLiteDatabase.beginTransaction(FrameworkSQLiteDatabase.java:69) at androidx.room.RoomDatabase.beginTransaction(RoomDatabase.java:354) at androidx.work.impl.WorkerWrapper.runWorker(WorkerWrapper.java:135) at androidx.work.impl.WorkerWrapper.run(WorkerWrapper.java:127) at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) at java.lang.Thread.run(Thread.java:818)
  6. ADVM #11 9 Fatal Exception: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code

    5) at android.database.sqlite.SQLiteConnection.nativeExecute(SQLiteConnection.java) at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:553) at android.database.sqlite.SQLiteSession.beginTransactionUnchecked(SQLiteSession.java:323) at android.database.sqlite.SQLiteSession.beginTransaction(SQLiteSession.java:298) at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:505) at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:416) at androidx.sqlite.db.framework.FrameworkSQLiteDatabase.beginTransaction(FrameworkSQLiteDatabase.java:69) at androidx.room.RoomDatabase.beginTransaction(RoomDatabase.java:354) at androidx.work.impl.WorkerWrapper.runWorker(WorkerWrapper.java:135) at androidx.work.impl.WorkerWrapper.run(WorkerWrapper.java:127) at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) at java.lang.Thread.run(Thread.java:818) Room
  7. ADVM #11 9 Fatal Exception: android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code

    5) at android.database.sqlite.SQLiteConnection.nativeExecute(SQLiteConnection.java) at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:553) at android.database.sqlite.SQLiteSession.beginTransactionUnchecked(SQLiteSession.java:323) at android.database.sqlite.SQLiteSession.beginTransaction(SQLiteSession.java:298) at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:505) at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:416) at androidx.sqlite.db.framework.FrameworkSQLiteDatabase.beginTransaction(FrameworkSQLiteDatabase.java:69) at androidx.room.RoomDatabase.beginTransaction(RoomDatabase.java:354) at androidx.work.impl.WorkerWrapper.runWorker(WorkerWrapper.java:135) at androidx.work.impl.WorkerWrapper.run(WorkerWrapper.java:127) at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) at java.lang.Thread.run(Thread.java:818) Room WorkManager
  8. ADVM #11 20 <activity android:name="com.google.android.play.core.missingsplits.PlayCoreMissingSplitsActivity" android:process=":playcore_missing_splits_activity"/> <activity android:name="com.google.android.play.core.common.PlayCoreDialogWrapperActivity" android:process=":playcore_dialog_wrapper_activity"> <service

    android:name="com.yandex.metrica.MetricaService" android:process=":Metrica" > <service android:name="com.yandex.metrica.ConfigurationService" android:process=":Metrica" > <service android:name="com.yandex.metrica.ConfigurationJobService" android:process=":Metrica" > 1 2 3
  9. ADVM #11 22 1 2 Пытался найти проблему в Я.Метрике

    Пытался найти проблему в нашем коде ❌
  10. ADVM #11 22 1 2 Пытался найти проблему в Я.Метрике

    Пытался найти проблему в нашем коде ❌ ❌
  11. ADVM #11 22 Пытался найти проблему в WorkManager 1 2

    3 Пытался найти проблему в Я.Метрике Пытался найти проблему в нашем коде ❌ ❌
  12. ADVM #11 22 Пытался найти проблему в WorkManager 1 2

    3 Пытался найти проблему в Я.Метрике Пытался найти проблему в нашем коде ❌ ❌ ❌
  13. ADVM #11 22 Пытался найти проблему в WorkManager 1 2

    3 Пытался найти проблему в Я.Метрике Пытался найти проблему в нашем коде Пытался найти проблему в Room 4 ❌ ❌ ❌
  14. ADVM #11 22 Пытался найти проблему в WorkManager 1 2

    3 Пытался найти проблему в Я.Метрике Пытался найти проблему в нашем коде Пытался найти проблему в Room 4 ❌ ❌ ❌ ❌
  15. ADVM #11 26 open class AvitoApp : BaseApp() { @Inject

    lateinit var analytics: Analytics override fun onCreate() { super.onCreate() // ... analytics.track(AppStartEvent()) // ... } // ... }
  16. ADVM #11 26 open class AvitoApp : BaseApp() { @Inject

    lateinit var analytics: Analytics override fun onCreate() { super.onCreate() // ... analytics.track(AppStartEvent()) // ... } // ... }
  17. ADVM #11 28 Zygote Process #1 ApplicaCon override fun onCreate()

    { // ... } fork() Process #2 ApplicaCon override fun onCreate() { // ... } Process #3 ApplicaCon override fun onCreate() { // ... } fork() fork()
  18. SQLite ADVM #11 29 analytics.track android-job WorkManager Room Process #1

    analytics.track android-job WorkManager Room Process #2
  19. ADVM #11 31 fun Application.runIfPrimaryProcess(block: () -> Unit) { if

    (isPrimaryProcess) { block() } } val Application.isPrimaryProcess: Boolean get() = currentProcessFullName == packageName val Application.currentProcessFullName: String? get() { val myPid = Process.myPid() val am = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager? return am?.runningAppProcesses?.find { myPid == it.pid }?.processName }
  20. ADVM #11 31 fun Application.runIfPrimaryProcess(block: () -> Unit) { if

    (isPrimaryProcess) { block() } } val Application.isPrimaryProcess: Boolean get() = currentProcessFullName == packageName val Application.currentProcessFullName: String? get() { val myPid = Process.myPid() val am = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager? return am?.runningAppProcesses?.find { myPid == it.pid }?.processName }
  21. ADVM #11 31 fun Application.runIfPrimaryProcess(block: () -> Unit) { if

    (isPrimaryProcess) { block() } } val Application.isPrimaryProcess: Boolean get() = currentProcessFullName == packageName val Application.currentProcessFullName: String? get() { val myPid = Process.myPid() val am = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager? return am?.runningAppProcesses?.find { myPid == it.pid }?.processName }
  22. ADVM #11 31 fun Application.runIfPrimaryProcess(block: () -> Unit) { if

    (isPrimaryProcess) { block() } } val Application.isPrimaryProcess: Boolean get() = currentProcessFullName == packageName val Application.currentProcessFullName: String? get() { val myPid = Process.myPid() val am = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager? return am?.runningAppProcesses?.find { myPid == it.pid }?.processName }
  23. ADVM #11 31 fun Application.runIfPrimaryProcess(block: () -> Unit) { if

    (isPrimaryProcess) { block() } } val Application.isPrimaryProcess: Boolean get() = currentProcessFullName == packageName val Application.currentProcessFullName: String? get() { val myPid = Process.myPid() val am = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager? return am?.runningAppProcesses?.find { myPid == it.pid }?.processName }
  24. ADVM #11 31 fun Application.runIfPrimaryProcess(block: () -> Unit) { if

    (isPrimaryProcess) { block() } } val Application.isPrimaryProcess: Boolean get() = currentProcessFullName == packageName val Application.currentProcessFullName: String? get() { val myPid = Process.myPid() val am = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager? return am?.runningAppProcesses?.find { myPid == it.pid }?.processName }
  25. ADVM #11 32 override fun onCreate() { super.onCreate() runIfPrimaryProcess {

    // do initialization here } } 1 Код — документация
  26. ADVM #11 32 override fun onCreate() { super.onCreate() runIfPrimaryProcess {

    // do initialization here } } 1 2 Код — документация Явно выражает намерение
  27. ADVM #11 32 override fun onCreate() { super.onCreate() runIfPrimaryProcess {

    // do initialization here } } Нельзя забыть про процессы 1 2 3 Код — документация Явно выражает намерение