Почему sqlite позволяет второму процессу выполнять операцию записи, в то время как первый процесс уже выполняет запись - PullRequest
4 голосов
/ 24 сентября 2019

Я написал два кода процесса, оба кода скопированы ниже, давайте предположим, что sqlitep1.c ссылается на p1, а sqlitep2.c ссылается на p2

p1 постоянно читает из файла базы данных sample.db, а p2 записывает вфайл базы данных sample.db,

Я выполнил один экземпляр p2 и p3 и один экземпляр p1, согласно документации sqlite, два процесса не могут одновременно записывать в sqlite.

в коде sqlitep2.ciесть две версии: одна открывает соединение и выполняет запись, а затем закрывает (p2), а другая - открывает соединение и записывает файл db и блокирует с помощью while (1), как показано во второй версии sqlitep2.c, которая выполняется как p3,

Сначала я выполнил p3, а затем p2. В соответствии с кодом p3 будет блокироваться после записи, в этот момент я предполагаю, что блокировка sqlite не снимается, поскольку соединение не закрывается.

Но в моем результатеЯ вижу, что p2 может писать без получения сообщения об ошибке занятости, даже если p3 не снял блокировку.

Примечание. Выполняется на машине linux.

sqlitep1.c

#include <stdio.h>
#include </sqlite/sqlite3.h>

int main()
{
    sqlite3_stmt *pSqlStmt = NULL;
    sqlite3 *pSqlHandle = NULL;
    int ret = SQLITE_ERROR;
    char *pcSharedPath = "sample.db";
    char* pcSqlQuery = NULL;
    char *name = NULL;
    int retry_count = 0;

    while (1)
    {
        printf ("process 1.....\n");
        printf ("-----------------------------\n\n");
            ret = SQLITE_ERROR;
        /*open connection to sqlite*/
        while (ret != SQLITE_OK) {
            printf ("process 1, open connection\n");
            ret = sqlite3_open_v2 (pcSharedPath, &(pSqlHandle), (SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX), NULL);
            if (ret != SQLITE_OK)
            {
                printf ("process 1, opening failed....\n");
                if (ret == SQLITE_BUSY)
                {
                    printf ("process 1, open connection busy error....\n");
                }
            }
            sleep(1);
        }
        printf ("process 1, database connection opened...\n");
               /* prepare query */
        ret = SQLITE_ERROR;
        pcSqlQuery = "SELECT * FROM EMP";
        while (ret != SQLITE_OK)
        {
            ret = sqlite3_prepare_v2 (pSqlHandle, pcSqlQuery, -1,  &pSqlStmt, NULL);
            if (ret == SQLITE_BUSY) {
                printf ("process 1, prepare busy error....\n");
            }
            if (ret == SQLITE_ERROR) {
                printf("SQLITE_ERROR\n");
            }
            sleep(1);
        }
        printf ("process 1, prepare success...\n");
        /* extract result from query */
        while(1)
        {
            ret = sqlite3_step (pSqlStmt);
            if (ret == SQLITE_DONE)
                break;
            if (ret != SQLITE_ROW) {
                printf("process 1, no row exists...\n");
                break;
            }
            name = sqlite3_column_text (pSqlStmt, 1);
            printf ("%s \n", name);
        }

        /* finalize */
        if (NULL != pSqlStmt)
        {
    ret = SQLITE_ERROR;
            // while (ret != SQLITE_OK) {
                ret = sqlite3_finalize (pSqlStmt);
                printf ("process 1, Finalizing %d...\n", ret);
            // }
            pSqlStmt = NULL;
        }
        /* close sqlite connection */
        ret = SQLITE_ERROR;
            retry_count = 0;
            while (ret != SQLITE_OK && retry_count != 5) {
          ret = sqlite3_close(pSqlHandle);
                printf("sqlite3_close %d...\n", ret);
                retry_count++;
            }
            retry_count=0;
        if (SQLITE_OK != ret) {
            printf ("sqlite close failed....Exiting process 1...\n");
            return 0;
        }
    }
}

первая версия sqlitep2.c, которая не блокируется после операции записи (no while (1))

#include <stdio.h>
#include </sqlite/sqlite3.h>
#include <sys/time.h>

int main(int argc, char *argv[])
{
    sqlite3_stmt *pSqlStmt = NULL;
    sqlite3 *pSqlHandle = NULL;
    int ret = SQLITE_ERROR;
    char *pcSharedPath = "sample.db";
    char pcSqlQuery[100] = "";
    char *name = NULL;
    int i = atoi(argv[1]);
    int retry_count = 0;

    while (1)
    {
        printf ("process 2.....\n");
        printf ("-----------------------------\n\n");
        ret = SQLITE_ERROR;
        /*open connection to sqlite*/
        while (ret != SQLITE_OK) {
            printf ("process 2, open connection\n");
            ret = sqlite3_open_v2 (pcSharedPath, &(pSqlHandle), (SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX), NULL);
            if (ret != SQLITE_OK)
            {
                printf ("process 2, opening failed %d....\n", ret);
                if (ret == SQLITE_BUSY)
                {
                    printf ("process 2, open connection busy error....\n");
                }
            }
            usleep(10*1000);
        }
        printf ("process 2, database connection opened...\n");
        sqlite3_busy_timeout(pSqlHandle, 1000);

    /* prepare query */
    ret = SQLITE_ERROR;
        char name[50];
    while (ret != SQLITE_OK && retry_count != 10)
    {
                pSqlStmt = NULL;
                sqlite3_snprintf(50, name, "\"Sachin%d\"", i);
                sqlite3_snprintf(100,pcSqlQuery, "INSERT INTO EMP VALUES (%d, %s)", i, name);
                printf ("%s\n", pcSqlQuery);
          ret = sqlite3_prepare_v2 (pSqlHandle, "INSERT INTO EMP(ID, NAME) VALUES (?1, ?2);", -1,  &pSqlStmt, NULL);
                sqlite3_bind_int(pSqlStmt, 1, i);
                sqlite3_bind_text(pSqlStmt, 2, name, -1, SQLITE_STATIC);
          if (ret == SQLITE_BUSY) {
              printf ("process 2, prepare busy error....\n");
          }
          else {
                if (ret == SQLITE_ERROR)
                {
               printf ("SQLITE_ERROR...\n");
                }
            ret = SQLITE_ERROR;
            while (ret != SQLITE_OK) {
                    ret = sqlite3_step(pSqlStmt);
                printf ("process 2, return from sqlite3_step : %d...\n", ret);
                    if (ret != SQLITE_DONE) {
                            printf ("process 2, insert error...\n");
                    } else if (ret == SQLITE_BUSY) {
                    printf("sqlite3_step busy error...\n");
                } else {
                               i++;
                        ret = SQLITE_OK;
                    }
            }
          }
                printf ("process 2, ret value of insert op %d\n ", ret);
            usleep(10*1000);
                retry_count++;
      }
      retry_count=0;
      printf ("process 2, prepare success...\n");
      /* finalize */
      if (NULL != pSqlStmt)
      {
            ret = SQLITE_ERROR;
            while (ret != SQLITE_OK) {
                ret = sqlite3_finalize (pSqlStmt);
                printf ("process 2, Finalizing %d...\n", ret);
            }
          pSqlStmt = NULL;
      }
      /* close sqlite connection */
            ret = SQLITE_ERROR;
            retry_count = 0;
        ret = sqlite3_close(pSqlHandle);
            while (ret != SQLITE_OK) {
          ret = sqlite3_close(pSqlHandle);
                printf("sqlite3_close %d...\n", ret);
                retry_count++;
            sleep(1);
            }
            retry_count=0;
            pSqlHandle = NULL;
   if (SQLITE_OK != ret) {
            printf ("sqlite close failed....Exiting process 2...\n");
            return 0;
        }
            sleep(1);
    }
}

вторая версия sqlitep2.c, которая имеет бесконечный блок while (1) после операции записиЭто означает, что он не снимает блокировку.

#include <stdio.h>
#include </sqlite/sqlite3.h>
#include <sys/time.h>

int main(int argc, char *argv[])
{
    sqlite3_stmt *pSqlStmt = NULL;
    sqlite3 *pSqlHandle = NULL;
    int ret = SQLITE_ERROR;
    char *pcSharedPath = "sample.db";
    char pcSqlQuery[100] = "";
    char *name = NULL;
    int i = atoi(argv[1]);
    int retry_count = 0;

    while (1)
    {
        printf ("process 2.....\n");
        printf ("-----------------------------\n\n");
        ret = SQLITE_ERROR;
        /*open connection to sqlite*/
        while (ret != SQLITE_OK) {
            printf ("process 2, open connection\n");
            ret = sqlite3_open_v2 (pcSharedPath, &(pSqlHandle), (SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX), NULL);
            if (ret != SQLITE_OK)
            {
                printf ("process 2, opening failed %d....\n", ret);
                if (ret == SQLITE_BUSY)
                {
                    printf ("process 2, open connection busy error....\n");
                }
            }
            usleep(10*1000);
        }
        printf ("process 2, database connection opened...\n");
        sqlite3_busy_timeout(pSqlHandle, 1000);

    /* prepare query */
    ret = SQLITE_ERROR;
        char name[50];
    while (ret != SQLITE_OK && retry_count != 10)
    {
                pSqlStmt = NULL;
                sqlite3_snprintf(50, name, "\"Sachin%d\"", i);
                sqlite3_snprintf(100,pcSqlQuery, "INSERT INTO EMP VALUES (%d, %s)", i, name);
                printf ("%s\n", pcSqlQuery);
          ret = sqlite3_prepare_v2 (pSqlHandle, "INSERT INTO EMP(ID, NAME) VALUES (?1, ?2);", -1,  &pSqlStmt, NULL);
                sqlite3_bind_int(pSqlStmt, 1, i);
                sqlite3_bind_text(pSqlStmt, 2, name, -1, SQLITE_STATIC);
          if (ret == SQLITE_BUSY) {
              printf ("process 2, prepare busy error....\n");
          }
          else {
                if (ret == SQLITE_ERROR)
                {
               printf ("SQLITE_ERROR...\n");
                }
            ret = SQLITE_ERROR;
            while (ret != SQLITE_OK) {
                    ret = sqlite3_step(pSqlStmt);
                printf ("process 2, return from sqlite3_step : %d...\n", ret);
                    if (ret != SQLITE_DONE) {
                            printf ("process 2, insert error...\n");
                    } else if (ret == SQLITE_BUSY) {
                    printf("sqlite3_step busy error...\n");
                } else {
                               i++;
                        ret = SQLITE_OK;
                    }
            }
          }
                printf ("process 2, ret value of insert op %d\n ", ret);
            usleep(10*1000);
                retry_count++;
      }
      while (1) {} // block here, DO NOT proceed further.
      retry_count=0;
      printf ("process 2, prepare success...\n");
      /* finalize */
      if (NULL != pSqlStmt)
      {
            ret = SQLITE_ERROR;
            while (ret != SQLITE_OK) {
                ret = sqlite3_finalize (pSqlStmt);
                printf ("process 2, Finalizing %d...\n", ret);
            }
          pSqlStmt = NULL;
      }
      /* close sqlite connection */
            ret = SQLITE_ERROR;
            retry_count = 0;
        ret = sqlite3_close(pSqlHandle);
            while (ret != SQLITE_OK) {
          ret = sqlite3_close(pSqlHandle);
                printf("sqlite3_close %d...\n", ret);
                retry_count++;
            sleep(1);
            }
            retry_count=0;
            pSqlHandle = NULL;
   if (SQLITE_OK != ret) {
            printf ("sqlite close failed....Exiting process 2...\n");
            return 0;
        }
            sleep(1);
    }
}

1 Ответ

0 голосов
/ 24 сентября 2019

SQLite блокирует транзакции, а не открытые соединения.Без явной транзакции (которую вы, похоже, не делаете) каждая инструкция SQL является отдельной транзакцией, которая заканчивается при выполнении инструкции.То, что вы получили незавершенное утверждение, ничего не значит, так как «шаг» выполнил вашу INSERT и снял блокировку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...