Почему файл WAL (-wal) может исчезнуть?

#android #c #sqlite #wal

Вопрос:

У меня есть приложение для Android, использующее SQLite через мост c (с использованием JNI/NDK). Я пытаюсь хранить данные в базе данных SQLite с включенным режимом WAL. Однако в большинстве случаев файл WAL либо отсутствует, либо пуст (размер 0 байт). Почему это может произойти?

Подробные сведения:

  • PRAGMA journal_mode=WAL;
  • 1 подключение для чтения и записи (однако операции с БД — инициализация, вставка, выбор — выполняются в разных потоках. Однако все операции выполняются последовательно. Это не ситуация, когда одновременно работает более одного читателя/автора).
  • Если я выполняю контрольную точку PRAGMA wal_checkpoint; , все транзакции, которые я вставил, переносятся из журнала в основной файл БД, даже если файл WAL визуально пуст/отсутствует.
  • Транзакции совершаются вручную с использованием BEGIN TRANSACTION; и END TRANSACTION; , без автоматической фиксации
  • Я проверяю состояние файла с помощью проводника файлов устройства в Android studio (также я пытался скопировать файлы журнала из внутреннего хранилища во внешнее).
  • Файл БД и -wal / -shm файлы находятся во внутренней памяти: /data/data/com.package.my/files/
  • Транзакции фактически передаются в файл WAL — я проверил, настроив a sqlite3_wal_hook : он показывает, что количество страниц увеличивается
  • sqlite 3.27.2
  • Android 7 (устройство Ciontek CS10)
  • fs = ext4
  • Версия NDK=21.4.7075529

Что я пробовал:

  • Сериализованный потокобезопасный режим ( SQLITE_OPEN_FULLMUTEX )
  • PRAGMA locking_mode=EXCLUSIVE; PRAGMA synchronous=EXTRA;
  • PRAGMA synchronous=FULL;
  • установка VFS в unix-excl
  • изменение каталога файлов на /data/data/com.package.my/files/db (чтобы иметь папку только для файлов БД, так как /файлы содержат другие файлы)
  • создание минимального воспроизводимого фрагмента. Часть проблемы заключается в том, что я попытался составить минимально воспроизводимый пример: сделал копию проекта только с частью БД (удалил все, кроме кода инициализации/DAO/сущности БД). И это свернутое приложение не смогло воспроизвести проблему: -файл wal всегда существует и не сжимается/не исчезает. Поэтому я в замешательстве, не могу понять, какая часть первоначального проекта вызывает проблемы.

Код:

 int wal_hook(void* userdata, sqlite3* handle, const char* dbFilename, int nPages){

    char* pChar;
    pChar = (char*)userdata; // "test"

    printf("Hello hook");
    return SQLITE_OK;
}

// DB init (executed once on app start)
void initDB() 
    int32 rc = sqlite3_open(db_name.c_str(), amp;handle); // rc = 0
//    int32 rc = sqlite3_open_v2(filename.c_str(), amp;handle, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, nullptr);
//    int rc = sqlite3_open_v2(filename.c_str(), amp;handle, SQLITE_OPEN_READWRITE, "unix-excl");

    // check threadsafe mode
    int stResult = sqlite3_threadsafe(); // stResult = 1

    // register WAL hook
    char* pointerContext = new char[4]();
    pointerContext[0] = 't';
    pointerContext[1] = 'e';
    pointerContext[2] = 's';
    pointerContext[3] = 't';
    sqlite3_wal_hook(handle, wal_hook, pointerContext);

    // turn WAL mode on
    int32 rcWAL = sqlite3_exec(handle, "PRAGMA journal_mode=WAL;", processResults, amp;result, amp;errMsg); // rcWAL = 0
}

// close connection
int32 close() {
    return sqlite3_close(handle);
}

// WAL checkpoint
sqlite3_exec(handle, "pragma wal_checkpoint;", processResults, amp;result, amp;errMsg);

// Insert
EventPtr persist(EventPtr obj) {
    vector<DBData*> args;
    int beginResult = sqlite3_exec(_connector->handle, "BEGIN TRANSACTION;", NULL, NULL, NULL);

    try {
        args.push_back(amp;obj->eventId);
        // ... push more args

        string query = "insert into events values(?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12,?13,?14);";
        int32_t rc = _connector->executePreparedWOresult(query.c_str(),amp;args);
        if(rc == SQLITE_DONE) {
            int endResult = sqlite3_exec(_connector->handle, "END TRANSACTION;", NULL, NULL, NULL);
            return obj;
        }
    } catch(...){ }
}

// SELECT
vector<EventPtr> readAll()
{
    string query = "select * from Events;";
    ResultSetPtr result= _connector->executePrepared(query.c_str(), NULL);
    vector<EventPtr> vec;
    for(int32_t i = 0; i < result->size(); i   ){
        EventPtr event(new Event);
        // init event
        vec.push_back(EventPtr(event));
    }
    return vec;
}

// executePreparedWOresult
int32 executePreparedWOresult(const stringamp; query, vector<DBData*> *args){
    sqlite3_stmt *stmt;
    cout << query ;
    sqlite3_prepare_v2(handle, query.c_str(), query.size(), amp;stmt, NULL);

    for(uint32 i = 0;args amp;amp; i < args->size(); i   ){
        switch(args->at(i)->getType()){
              // statement bindings (sqlite3_bind_text, etc.) 
        }
    }

    int32 rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    return rc;
}

// executePrepared
ResultSetPtr executePrepared(const char *query, const vector<DBData*> *args){
    ResultSetPtr res = ResultSetPtr(new ResultSet);
    sqlite3_stmt *stmt;
    int32_t rs = sqlite3_prepare_v2(handle, query, strlen(query), amp;stmt, NULL);

    for(uint32 i = 0;args amp;amp; i < args->size(); i   ){
        switch(args->at(i)->getType()){
              // statement bindings (sqlite3_bind_text, etc.) 
        }
    }
    int32 count = sqlite3_column_count(stmt);
    int32 rc;
    ResultRow row;
    int32 rows = 0;
    while((rc = sqlite3_step(stmt)) == SQLITE_ROW){
         rows   ;
         for(int32 i = 0; i < count; i  ){
              // multiple row.push_back-s for all columns
         }
         res->push_back(row);
         row.clear();
    }
    sqlite3_finalize(stmt);
    return res;
}