2014-11-03 3 views
0

Я выполняю тысячи вычислений с помощью R и некоторого внешнего программного обеспечения. Чтобы отслеживать это, я построил конвейер в R в базе данных SQLite3. Чтобы все было сделано, я настроил его, чтобы позволить нескольким узлам нашего вычислительного кластера запускать R-скрипт. Естественно, нам нужно держать вещи распыленными, поэтому я активно начинаю транзакции.Сбой транзакций SQLite

Чтобы избежать сценария от сбоя при попытке получить блокировку на базе данных, я следующий код, который пытается начать транзакцию, а если не удается, жду, а затем повторю:

dbBeginTransaction <- function(dbconn, state='DEFERRED', timeout=5, retries=10) { 
    state <- toupper(state) 
    if (!state %in% c('DEFERRED','IMMEDIATE','EXCLUSIVE')) stop('Attempt at illegal transaction.') 
    res <- NULL 
    exit <- FALSE 
    for (i in 1:retries) { 
    try(
     res <- dbSendQuery(dbconn, paste('BEGIN',state,'TRANSACTION;')) 
     , silent=TRUE 
    ) 
    # res is null if above query fails. 
    err <- dbGetException(dbconn) 
    if (err$errorNum == 0) { ## OK 
     #return(TRUE) 
     exit <- TRUE 
     break 
    } else if (err$errorNum == 5) { ## Database locked. 
     if (i == retries+1) { 
     cat('Database still locked after',i,'attempts.\n',file=stderr()) 
     #return(FALSE) 
     exit <- FALSE 
     break 
     } else { 
     Sys.sleep(timeout) 
     } 
    } else { 
     ## errorNum == 1 ## Already within transaction. 
     cat(err$errorMsg, '\n', file = stderr()) 
     #return(FALSE) 
     exit <- FALSE 
     break 
    } 
    } 
    invisible(exit) 
} 


    ## Usage: 
    insert_results <- function(results) { 
    ## Some preparing of results 
    if (dbBeginTransaction(conn, 'EXCLUSIVE') == FALSE) return(FALSE) 
    dbSendPreparedQuery(conn, 'INSERT INTO results (...) VALUES (...);', results) 
    dbCommit(conn) 
    } 

    ## After a computation: 
    results <- magic() 
    if (!insert_results(results)) stop('Could not save results') 

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

Error in sqliteSendQuery(conn, statement, bind.data) : 
    rsqlite_query_send: could not execute: database is locked 
Calls: dbSendPreparedQuery ... dbSendPreparedQuery -> .local -> sqliteSendQuery -> .Call 
Execution halted 

Я не могу понять, что происходит, и я до сих пор воспроизвести ошибку. Ошибка ясна, но я бы подумал, что моя рутина предотвратила это.

Любые мысли о том, почему это происходит?

Я бегу R под Linux, как видно:

> sessionInfo() 
R version 3.1.2 (2014-10-31) 
Platform: x86_64-unknown-linux-gnu (64-bit) 

locale: 
[1] LC_CTYPE=en_US  LC_NUMERIC=C   LC_TIME=en_US 
[4] LC_COLLATE=en_US  LC_MONETARY=en_US LC_MESSAGES=en_US 
[7] LC_PAPER=en_US  LC_NAME=C   LC_ADDRESS=C 
[10] LC_TELEPHONE=C  LC_MEASUREMENT=en_US LC_IDENTIFICATION=C 

attached base packages: 
[1] stats  graphics grDevices utils  datasets methods base 

other attached packages: 
[1] RSQLite_1.0.0 DBI_0.3.1 
+0

Почему вы не просто использовать [ПРАГМА busy_timeout] (http://www.sqlite.org/pragma.html#pragma_busy_timeout)? –

+0

Потому что я, видимо, не прочитал документацию полностью. :) Насколько я могу читать, это обходит мою рутину dbBeginTransaction? – MrGumble

+0

Обновление: использование PRAGMA решило проблему, и я бросил свою функцию на кодовый свал. – MrGumble

ответ

-2

столкнулись с этой проблемой сегодня в офисе сегодня. Это предложение моего коллеги: его можно решить, убедившись, что каждый раз, когда вы подключаетесь к базе данных и делаете какие-то изменения в базе данных, вы делаете dbCommit (con). Новый код будет выглядеть следующим образом:

dbBeginTransaction <- function(dbconn, state='DEFERRED', timeout=5, retries=10) { 
    state <- toupper(state) 
    if (!state %in% c('DEFERRED','IMMEDIATE','EXCLUSIVE')) stop('Attempt at illegal transaction.') 
    res <- NULL 
    exit <- FALSE 
    for (i in 1:retries) { 
    try(
     dbBegin(dbconn) 
     res <- dbSendQuery(dbconn, paste('BEGIN',state,'TRANSACTION;')) 
     , silent=TRUE 
    ) 
    # res is null if above query fails. 
    err <- dbGetException(dbconn) 
    if (err$errorNum == 0) { ## OK 
     #return(TRUE) 
     exit <- TRUE 
     break 
    } else if (err$errorNum == 5) { ## Database locked. 
     if (i == retries+1) { 
     cat('Database still locked after',i,'attempts.\n',file=stderr()) 
     #return(FALSE) 
     exit <- FALSE 
     break 
     } else { 
     Sys.sleep(timeout) 
     } 
    } else { 
     ## errorNum == 1 ## Already within transaction. 
     cat(err$errorMsg, '\n', file = stderr()) 
     #return(FALSE) 
     exit <- FALSE 
     break 
    } 
    } 
    invisible(exit) 
} 


    ## Usage: 
    insert_results <- function(results) { 
    ## Some preparing of results 
    if (dbBeginTransaction(conn, 'EXCLUSIVE') == FALSE) return(FALSE) 
    dbSendPreparedQuery(conn, 'INSERT INTO results (...) VALUES (...);', results) 
    dbCommit(conn) 
    } 

    ## After a computation: 
    results <- magic() 
    if (!insert_results(results)) stop('Could not save results') 

И я решил, добавив dbBegin (CON) непосредственно перед началом любых операций с базой данных

dbBeginTransaction <- function(dbconn, state='DEFERRED', timeout=5, retries=10) { 
    state <- toupper(state) 
    if (!state %in% c('DEFERRED','IMMEDIATE','EXCLUSIVE')) stop('Attempt at illegal transaction.') 
    res <- NULL 
    exit <- FALSE 
    for (i in 1:retries) { 
    try(
     res <- dbSendQuery(dbconn, paste('BEGIN',state,'TRANSACTION;')) 
     , silent=TRUE 
     dbCommit(dbconn) 
    ) 
    # res is null if above query fails. 
    err <- dbGetException(dbconn) 
    if (err$errorNum == 0) { ## OK 
     #return(TRUE) 
     exit <- TRUE 
     break 
    } else if (err$errorNum == 5) { ## Database locked. 
     if (i == retries+1) { 
     cat('Database still locked after',i,'attempts.\n',file=stderr()) 
     #return(FALSE) 
     exit <- FALSE 
     break 
     } else { 
     Sys.sleep(timeout) 
     } 
    } else { 
     ## errorNum == 1 ## Already within transaction. 
     cat(err$errorMsg, '\n', file = stderr()) 
     #return(FALSE) 
     exit <- FALSE 
     break 
    } 
    } 
    invisible(exit) 
} 


    ## Usage: 
    insert_results <- function(results) { 
    ## Some preparing of results 
    if (dbBeginTransaction(conn, 'EXCLUSIVE') == FALSE) return(FALSE) 
    dbSendPreparedQuery(conn, 'INSERT INTO results (...) VALUES (...);', results) 
    dbCommit(conn) 
    } 

    ## After a computation: 
    results <- magic() 
    if (!insert_results(results)) stop('Could not save results') 
+0

'' dbBegin'' начинает транзакцию; следующий запрос '' BEGIN TRANSACTION; '' приведет к ошибке, так как вы не можете начать две транзакции («Ошибка: не удается запустить транзакцию в транзакции»). Следующая транзакция 'dbCommit' _ _ _ транзакция, оставив нас там, где мы начали, без транзакций. – MrGumble

+0

Заменить 'dbBeginTransaction' с' dbBegin' dbBeginTransaction - это, вероятно, старое, что сработает. – princelySid

+0

Я не использую старый '' dbBeginTransaction''; Я только ссылаюсь на свою собственную функцию '' dbBeginTransction''. – MrGumble

Смежные вопросы