Fossil SCM

Improved detection of potential read/write txn conflicts. Hold a write transaction on the server-side for the duration of an /xfer request, to avoid unexpected SQLITE_BUSY errors.

drh 2020-08-12 12:56 trunk
Commit d9543f4c2ca2f6ad356de58e8287d2c643787e1f32d957d15d26dc2a41e8158e
2 files changed +6 -3 +2 -2
+6 -3
--- src/db.c
+++ src/db.c
@@ -116,10 +116,11 @@
116116
*/
117117
static struct DbLocalData {
118118
int nBegin; /* Nesting depth of BEGIN */
119119
int doRollback; /* True to force a rollback */
120120
int nCommitHook; /* Number of commit hooks */
121
+ int wrTxn; /* Outer-most TNX is a write */
121122
Stmt *pAllStmt; /* List of all unfinalized statements */
122123
int nPrepare; /* Number of calls to sqlite3_prepare_v2() */
123124
int nDeleteOnFail; /* Number of entries in azDeleteOnFail[] */
124125
struct sCommitHook {
125126
int (*xHook)(void); /* Functions to call at db_end_transaction() */
@@ -195,10 +196,11 @@
195196
sqlite3_commit_hook(g.db, db_verify_at_commit, 0);
196197
db.nPriorChanges = sqlite3_total_changes(g.db);
197198
db.doRollback = 0;
198199
db.zStartFile = zStartFile;
199200
db.iStartLine = iStartLine;
201
+ db.wrTxn = 0;
200202
}
201203
db.nBegin++;
202204
}
203205
/*
204206
** Begin a new transaction for writing.
@@ -209,11 +211,12 @@
209211
sqlite3_commit_hook(g.db, db_verify_at_commit, 0);
210212
db.nPriorChanges = sqlite3_total_changes(g.db);
211213
db.doRollback = 0;
212214
db.zStartFile = zStartFile;
213215
db.iStartLine = iStartLine;
214
- }else{
216
+ db.wrTxn = 1;
217
+ }else if( !db.wrTxn ){
215218
fossil_warning("read txn at %s:%d might cause SQLITE_BUSY "
216219
"for the write txn at %s:%d",
217220
db.zStartFile, db.iStartLine, zStartFile, iStartLine);
218221
}
219222
db.nBegin++;
@@ -1328,11 +1331,11 @@
13281331
);
13291332
if( rc!=SQLITE_OK ){
13301333
db_err("[%s]: %s", zDbName, sqlite3_errmsg(db));
13311334
}
13321335
db_maybe_set_encryption_key(db, zDbName);
1333
- sqlite3_busy_timeout(db, 5000);
1336
+ sqlite3_busy_timeout(db, 15000);
13341337
sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */
13351338
sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0);
13361339
sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0);
13371340
sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0);
13381341
sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
@@ -1344,11 +1347,11 @@
13441347
);
13451348
if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0);
13461349
db_add_aux_functions(db);
13471350
re_add_sql_func(db); /* The REGEXP operator */
13481351
foci_register(db); /* The "files_of_checkin" virtual table */
1349
- sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
1352
+ sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc);
13501353
return db;
13511354
}
13521355
13531356
13541357
/*
13551358
--- src/db.c
+++ src/db.c
@@ -116,10 +116,11 @@
116 */
117 static struct DbLocalData {
118 int nBegin; /* Nesting depth of BEGIN */
119 int doRollback; /* True to force a rollback */
120 int nCommitHook; /* Number of commit hooks */
 
121 Stmt *pAllStmt; /* List of all unfinalized statements */
122 int nPrepare; /* Number of calls to sqlite3_prepare_v2() */
123 int nDeleteOnFail; /* Number of entries in azDeleteOnFail[] */
124 struct sCommitHook {
125 int (*xHook)(void); /* Functions to call at db_end_transaction() */
@@ -195,10 +196,11 @@
195 sqlite3_commit_hook(g.db, db_verify_at_commit, 0);
196 db.nPriorChanges = sqlite3_total_changes(g.db);
197 db.doRollback = 0;
198 db.zStartFile = zStartFile;
199 db.iStartLine = iStartLine;
 
200 }
201 db.nBegin++;
202 }
203 /*
204 ** Begin a new transaction for writing.
@@ -209,11 +211,12 @@
209 sqlite3_commit_hook(g.db, db_verify_at_commit, 0);
210 db.nPriorChanges = sqlite3_total_changes(g.db);
211 db.doRollback = 0;
212 db.zStartFile = zStartFile;
213 db.iStartLine = iStartLine;
214 }else{
 
215 fossil_warning("read txn at %s:%d might cause SQLITE_BUSY "
216 "for the write txn at %s:%d",
217 db.zStartFile, db.iStartLine, zStartFile, iStartLine);
218 }
219 db.nBegin++;
@@ -1328,11 +1331,11 @@
1328 );
1329 if( rc!=SQLITE_OK ){
1330 db_err("[%s]: %s", zDbName, sqlite3_errmsg(db));
1331 }
1332 db_maybe_set_encryption_key(db, zDbName);
1333 sqlite3_busy_timeout(db, 5000);
1334 sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */
1335 sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0);
1336 sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0);
1337 sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0);
1338 sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
@@ -1344,11 +1347,11 @@
1344 );
1345 if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0);
1346 db_add_aux_functions(db);
1347 re_add_sql_func(db); /* The REGEXP operator */
1348 foci_register(db); /* The "files_of_checkin" virtual table */
1349 sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
1350 return db;
1351 }
1352
1353
1354 /*
1355
--- src/db.c
+++ src/db.c
@@ -116,10 +116,11 @@
116 */
117 static struct DbLocalData {
118 int nBegin; /* Nesting depth of BEGIN */
119 int doRollback; /* True to force a rollback */
120 int nCommitHook; /* Number of commit hooks */
121 int wrTxn; /* Outer-most TNX is a write */
122 Stmt *pAllStmt; /* List of all unfinalized statements */
123 int nPrepare; /* Number of calls to sqlite3_prepare_v2() */
124 int nDeleteOnFail; /* Number of entries in azDeleteOnFail[] */
125 struct sCommitHook {
126 int (*xHook)(void); /* Functions to call at db_end_transaction() */
@@ -195,10 +196,11 @@
196 sqlite3_commit_hook(g.db, db_verify_at_commit, 0);
197 db.nPriorChanges = sqlite3_total_changes(g.db);
198 db.doRollback = 0;
199 db.zStartFile = zStartFile;
200 db.iStartLine = iStartLine;
201 db.wrTxn = 0;
202 }
203 db.nBegin++;
204 }
205 /*
206 ** Begin a new transaction for writing.
@@ -209,11 +211,12 @@
211 sqlite3_commit_hook(g.db, db_verify_at_commit, 0);
212 db.nPriorChanges = sqlite3_total_changes(g.db);
213 db.doRollback = 0;
214 db.zStartFile = zStartFile;
215 db.iStartLine = iStartLine;
216 db.wrTxn = 1;
217 }else if( !db.wrTxn ){
218 fossil_warning("read txn at %s:%d might cause SQLITE_BUSY "
219 "for the write txn at %s:%d",
220 db.zStartFile, db.iStartLine, zStartFile, iStartLine);
221 }
222 db.nBegin++;
@@ -1328,11 +1331,11 @@
1331 );
1332 if( rc!=SQLITE_OK ){
1333 db_err("[%s]: %s", zDbName, sqlite3_errmsg(db));
1334 }
1335 db_maybe_set_encryption_key(db, zDbName);
1336 sqlite3_busy_timeout(db, 15000);
1337 sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */
1338 sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0);
1339 sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0);
1340 sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0);
1341 sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
@@ -1344,11 +1347,11 @@
1347 );
1348 if( g.fSqlTrace ) sqlite3_trace_v2(db, SQLITE_TRACE_PROFILE, db_sql_trace, 0);
1349 db_add_aux_functions(db);
1350 re_add_sql_func(db); /* The REGEXP operator */
1351 foci_register(db); /* The "files_of_checkin" virtual table */
1352 sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_FKEY, 0, &rc);
1353 return db;
1354 }
1355
1356
1357 /*
1358
+2 -2
--- src/xfer.c
+++ src/xfer.c
@@ -1213,11 +1213,11 @@
12131213
xfer.maxTime = db_get_int("max-download-time", 30);
12141214
if( xfer.maxTime<1 ) xfer.maxTime = 1;
12151215
xfer.maxTime += time(NULL);
12161216
g.xferPanic = 1;
12171217
1218
- db_begin_transaction();
1218
+ db_begin_write();
12191219
db_multi_exec(
12201220
"CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
12211221
"CREATE TEMP TABLE unk(uuid TEXT PRIMARY KEY) WITHOUT ROWID;"
12221222
);
12231223
manifest_crosslink_begin();
@@ -1753,11 +1753,11 @@
17531753
*/
17541754
zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')");
17551755
@ # timestamp %s(zNow)
17561756
free(zNow);
17571757
1758
- db_end_transaction(0);
1758
+ db_commit_transaction();
17591759
configure_rebuild();
17601760
}
17611761
17621762
/*
17631763
** COMMAND: test-xfer
17641764
--- src/xfer.c
+++ src/xfer.c
@@ -1213,11 +1213,11 @@
1213 xfer.maxTime = db_get_int("max-download-time", 30);
1214 if( xfer.maxTime<1 ) xfer.maxTime = 1;
1215 xfer.maxTime += time(NULL);
1216 g.xferPanic = 1;
1217
1218 db_begin_transaction();
1219 db_multi_exec(
1220 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
1221 "CREATE TEMP TABLE unk(uuid TEXT PRIMARY KEY) WITHOUT ROWID;"
1222 );
1223 manifest_crosslink_begin();
@@ -1753,11 +1753,11 @@
1753 */
1754 zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')");
1755 @ # timestamp %s(zNow)
1756 free(zNow);
1757
1758 db_end_transaction(0);
1759 configure_rebuild();
1760 }
1761
1762 /*
1763 ** COMMAND: test-xfer
1764
--- src/xfer.c
+++ src/xfer.c
@@ -1213,11 +1213,11 @@
1213 xfer.maxTime = db_get_int("max-download-time", 30);
1214 if( xfer.maxTime<1 ) xfer.maxTime = 1;
1215 xfer.maxTime += time(NULL);
1216 g.xferPanic = 1;
1217
1218 db_begin_write();
1219 db_multi_exec(
1220 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
1221 "CREATE TEMP TABLE unk(uuid TEXT PRIMARY KEY) WITHOUT ROWID;"
1222 );
1223 manifest_crosslink_begin();
@@ -1753,11 +1753,11 @@
1753 */
1754 zNow = db_text(0, "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S', 'now')");
1755 @ # timestamp %s(zNow)
1756 free(zNow);
1757
1758 db_commit_transaction();
1759 configure_rebuild();
1760 }
1761
1762 /*
1763 ** COMMAND: test-xfer
1764

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button