Ошибка: база данных блокируется при вызове sqlite3_exe c () из fork () child - PullRequest
0 голосов
/ 08 января 2020

У меня есть программа сервер / клиент, где сервер порождает дочернего элемента с помощью fork () для каждого подключенного клиента. Затем клиент отправляет на сервер команды для выполнения определенных запросов к базе данных sqlite3 (INSERT INTO, SELECT, DELETE FROM). Примечание: Я открываю базу данных после fork (), как указано здесь: https://www.sqlite.org/howtocorrupt.html в 2.6

Проблема

Прекрасно работает, когда 1 клиент подключен, но когда есть 2 подключенных: когда один из них выполняет первый запрос, который изменяет базу данных (например, INSERT INTO), с этого момента только он может продолжать изменять. Остальные получают ошибку : база данных заблокирована . Даже после того, как клиент, блокирующий .db, завершает работу, другой по-прежнему не может измениться.

Я пытался:

  • PRAGMA journal_mode = wal в терминале sqlite3
  • выполнение вручную BEGIN TRANSACTION; до запроса INSERT и COMMIT; после
  • sqlite3_close (db) после запроса INSERT и повторное открытие после

Наблюдение:

Я знаю, что использование потоков, вероятно, будет более безопасным и решит проблему, но я хотел бы попробовать все с помощью fork () перед изменением Вся структура проекта.

Код сервера (я удалил ненужные части кода):

int main () {
   sqlite3* db;
   //code for connection...
   while (1) {
        client = accept(sd, (struct sockaddr *) &from, &length);
        if (-1 == (pid = fork())) {
            perror("Error at fork");
        }
        if (pid == 0) {
            if (sqlite3_open("./Database.db", &db))
            perror("Error: Could not open database.\n")
            while (1) {
                sprintf(query, "INSERT INTO table VALUES ('%s','%s','%s','%s','%s','%s');",string1, string2,string3,string4,string5,string6);
                if(sqlite3_exec(db, query, NULL, 0, &error) != SQLITE_OK){
                      printf("%s", sqlite3_errmsg(db));
                      fflush(stdout);
                }
         }
} 

Спасибо за потраченное время!

Ответы [ 2 ]

0 голосов
/ 09 января 2020

Ответ

Проблема заключалась в том, что база данных могла взаимодействовать только с одним процессом за раз, поэтому я открывал и закрывал базу данных до и после каждого запроса, и теперь она работает отлично. Единственная проблема, которая может появиться сейчас, - это если несколько процессов одновременно выполняют запрос, но это не может произойти в моей программе. Решение: Это можно исправить с помощью потоков и мьютекса вместо fork (). Спасибо за помощь

0 голосов
/ 08 января 2020

Вот минимальная тестовая программа, которая разветвляет несколько дочерних процессов, из которых все пытаются вставить отдельные строки за раз без транзакций в базу данных. Я не могу воспроизвести ваши результаты - даже при отсутствии занятого тайм-аута, более чем один процесс может вставить хотя бы некоторые данные (хотя в этом случае возникает много ошибок блокировки).

Это дает вам такое же поведение вы видите, когда пытаетесь это сделать?

#include <sqlite3.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define TRIALS 5

// The smaller the timeout, the more likely to get a locked error.
#define TIMEOUT 200

int main(void) {
  for (int n = 0; n < TRIALS; n++) {
    pid_t pid = fork();
    if (pid < 0) {
      perror("fork");
      return EXIT_FAILURE;
    }
    if (pid == 0) {
      sqlite3 *db;
      sqlite3_stmt *stmt;
      // test.db needs a table named test like:
      // CREATE TABLE test(a,b)
      if (sqlite3_open("test.db", &db) != SQLITE_OK) {
        fprintf(stderr, "sqlite3_open in child %d: %s\n", n,
                sqlite3_errmsg(db));
        sqlite3_close(db);
        return EXIT_FAILURE;
      }
      sqlite3_busy_timeout(db, TIMEOUT);
      if (sqlite3_prepare_v2(db, "INSERT INTO test VALUES (?, ?)", -1, &stmt,
                             NULL) != SQLITE_OK) {
        fprintf(stderr, "sqlite3_prepare_v2 in child %d: %s\n", n,
                sqlite3_errmsg(db));
        sqlite3_close(db);
        return EXIT_FAILURE;
      }
      sqlite3_bind_int(stmt, 1, n);
      for (int m = 0; m < TRIALS; m++) {
        sqlite3_bind_int(stmt, 2, m);
        if (sqlite3_step(stmt) != SQLITE_DONE) {
          fprintf(stderr, "sqlite3_step in child %d: %s\n", n,
                  sqlite3_errmsg(db));
        } else {
          fprintf(stderr, "child %d inserted %d\n", n, m);
        }
        sqlite3_reset(stmt);
        // Sleep for a short time to allow another process to run.
        // Otherwise this loop runs fast enough that it might as well
        // be done in serial, not parallel.
        unsigned int p;
        sqlite3_randomness(sizeof p, &p);
        sqlite3_sleep((p % 50) + 1);
      }
      sqlite3_finalize(stmt);
      sqlite3_close(db);
      return 0;
    }
  }
  for (int n = 0; n < TRIALS; n++) {
    wait(NULL);
  }
  return 0;
}
...