Skip to content

Commit 8d0ffb1

Browse files
committed
Add recover_deadlock sync for non-SI page lock waiter path
Signed-off-by: Dorin Hogea <dhogea@bloomberg.net>
1 parent 53d8544 commit 8d0ffb1

25 files changed

Lines changed: 466 additions & 84 deletions

bdb/bdb_api.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2497,8 +2497,17 @@ int bdb_queuedb_has_seq(bdb_state_type *);
24972497
void dispatch_waiting_clients(void);
24982498

24992499
struct sqlclntstate;
2500-
int release_locks_int(const char *trace, const char *func, int line, struct sqlclntstate *);
2501-
#define release_locks(trace) release_locks_int(trace, __func__, __LINE__, NULL)
2500+
typedef enum {
2501+
RLOCKS_REASON_SI_LOCKWAIT, /* page lock waiter on SI/serial session */
2502+
RLOCKS_REASON_LOCKWAIT, /* page lock waiter on non-SI session */
2503+
RLOCKS_REASON_RANDOM, /* random release (testing) */
2504+
RLOCKS_REASON_LOCK_DESIRED, /* global BDB write lock desired */
2505+
RLOCKS_REASON_EMIT_ROW, /* waiters at row emit */
2506+
RLOCKS_REASON_LONG_REPWAIT, /* long rep wait at row emit */
2507+
RLOCKS_REASON_SLOW_READER, /* slow reader */
2508+
} rlocks_reason_t;
2509+
int release_locks_int(rlocks_reason_t reason, const char *func, int line, struct sqlclntstate *);
2510+
#define release_locks(reason) release_locks_int(reason, __func__, __LINE__, NULL)
25022511

25032512
int bdb_keylen(bdb_state_type *bdb_state, int ixnum);
25042513

bdb/bdb_osqlcur.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -463,11 +463,11 @@ int bdb_osql_update_shadows(bdb_cursor_ifn_t *pcur_ifn, bdb_osql_trn_t *trn,
463463
logmsg(LOGMSG_WARN,
464464
"%s: releasing locks while updating shadows\n",
465465
__func__);
466-
rc = release_locks("update shadows");
466+
rc = release_locks(RLOCKS_REASON_SI_LOCKWAIT);
467467
released_locks = 1;
468468
} else if (gbl_sql_random_release_interval &&
469469
!(rand() % gbl_sql_random_release_interval)) {
470-
rc = release_locks("random release update shadows");
470+
rc = release_locks(RLOCKS_REASON_RANDOM);
471471
released_locks = 1;
472472
}
473473
if (rc != 0) {

db/db_tunables.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,8 @@ extern int gbl_create_default_user;
312312
extern int gbl_allow_neg_column_size;
313313
extern int gbl_client_heartbeat_ms;
314314
extern int gbl_rep_wait_release_ms;
315+
extern int gbl_debug_sleep_in_cursor_move;
316+
extern int gbl_recover_deadlock_sync_dta;
315317
extern int gbl_rep_wait_core_ms;
316318
extern int gbl_random_get_curtran_failures;
317319
extern int gbl_txn_fop_noblock;

db/db_tunables.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1750,6 +1750,13 @@ REGISTER_TUNABLE("client_heartbeat_ms",
17501750
TUNABLE_INTEGER, &gbl_client_heartbeat_ms,
17511751
EXPERIMENTAL | INTERNAL, NULL, NULL, NULL, NULL);
17521752

1753+
REGISTER_TUNABLE("debug_sleep_in_cursor_move", "Sleep N ms on each cursor move (testing only). (Default: 0)",
1754+
TUNABLE_INTEGER, &gbl_debug_sleep_in_cursor_move, EXPERIMENTAL | INTERNAL, NULL, NULL, NULL, NULL);
1755+
1756+
REGISTER_TUNABLE("recover_deadlock_sync_dta",
1757+
"Sync index/data cursors before lock release in recover_deadlock. (Default: 0)", TUNABLE_BOOLEAN,
1758+
&gbl_recover_deadlock_sync_dta, EXPERIMENTAL | INTERNAL, NULL, NULL, NULL, NULL);
1759+
17531760
REGISTER_TUNABLE("rep_release_wait_ms",
17541761
"Release sql-locks if rep-thd is blocked for this many ms."
17551762
" (Default: 60000)",

db/sql.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,6 +1291,7 @@ struct BtCursor {
12911291
void *query_preparer_data;
12921292

12931293
int permissions; /* permissions for read/write access to table */
1294+
BtCursor *pCursorHintTableCursor;
12941295
};
12951296

12961297
struct sql_hist {
@@ -1505,6 +1506,7 @@ int sqlite3LockStmtTables(sqlite3_stmt *pStmt);
15051506
int sqlite3UnlockStmtTablesRemotes(struct sqlclntstate *clnt);
15061507
void sql_remote_schema_changed(struct sqlclntstate *clnt, sqlite3_stmt *pStmt);
15071508
int release_locks_on_emit_row(struct sqlclntstate *clnt);
1509+
void sync_index_data_cursors(struct sql_thread *thd);
15081510

15091511
void clearClientSideRow(struct sqlclntstate *clnt);
15101512
struct temptable get_tbl_by_rootpg(const sqlite3 *, int);

db/sqlglue.c

Lines changed: 88 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3246,6 +3246,54 @@ static inline int sqlite3VdbeCompareRecordPacked(KeyInfo *pKeyInfo, int k1len,
32463246
}
32473247

32483248
unsigned long long release_locks_on_si_lockwait_cnt = 0;
3249+
int gbl_debug_sleep_in_cursor_move = 0; /* ms to sleep on each cursor move (testing only) */
3250+
int gbl_recover_deadlock_sync_dta = 0; /* sync index/data cursors before lock release */
3251+
3252+
/*
3253+
* Before releasing cursor locks, walk all open index cursors and sync each
3254+
* one's paired data cursor (set via BTREE_HINT_TABLECURSOR) to the index
3255+
* cursor's current genid if they differ. Must be called while locks are
3256+
* still held so the target row is guaranteed to exist at find time.
3257+
*/
3258+
void sync_index_data_cursors(struct sql_thread *thd)
3259+
{
3260+
BtCursor *cur;
3261+
3262+
Pthread_mutex_lock(&thd->lk);
3263+
if (!thd->bt)
3264+
goto done;
3265+
3266+
LISTC_FOR_EACH(&thd->bt->cursors, cur, lnk)
3267+
{
3268+
BtCursor *dta = cur->pCursorHintTableCursor;
3269+
if (!dta || !cur->bdbcur || !dta->bdbcur)
3270+
continue;
3271+
if (cur->cursor_class != CURSORCLASS_INDEX)
3272+
continue;
3273+
unsigned long long idx_genid = cur->genid;
3274+
if (idx_genid == 0 || idx_genid == dta->genid)
3275+
continue;
3276+
/* data cursor hasn't caught up to the index cursor -- find it now.
3277+
* use bdbcur->find directly (not ddguard) to avoid re-entering
3278+
* recover_deadlock which also needs thd->lk */
3279+
int bdberr;
3280+
int rc = dta->bdbcur->find(dta->bdbcur, &idx_genid, sizeof(idx_genid), 0, &bdberr);
3281+
if (rc != IX_FND)
3282+
continue;
3283+
int fndlen;
3284+
void *buf;
3285+
uint8_t ver;
3286+
dta->bdbcur->get_found_data(dta->bdbcur, &dta->rrn, &dta->genid, &fndlen, &buf, &ver);
3287+
vtag_to_ondisk(dta->db, buf, &fndlen, ver, dta->genid);
3288+
dta->ondisk_buf = buf;
3289+
dta->dtabuf = buf;
3290+
dta->dtabuflen = fndlen;
3291+
}
3292+
3293+
done:
3294+
Pthread_mutex_unlock(&thd->lk);
3295+
}
3296+
32493297
/* Release pagelocks if the replicant is waiting on this sql thread */
32503298
static int cursor_move_postop(BtCursor *pCur)
32513299
{
@@ -3255,15 +3303,22 @@ static int cursor_move_postop(BtCursor *pCur)
32553303
extern int gbl_locks_check_waiters;
32563304
int rc = 0;
32573305

3258-
/* FIXME modsnap does not handle repositioning correctly? */
3259-
if (gbl_locks_check_waiters && gbl_sql_release_locks_on_si_lockwait && clnt->dbtran.mode == TRANLEVEL_SERIAL) {
3306+
if (gbl_debug_sleep_in_cursor_move)
3307+
poll(NULL, 0, gbl_debug_sleep_in_cursor_move);
3308+
3309+
if (gbl_locks_check_waiters) {
32603310
extern int gbl_sql_random_release_interval;
3311+
/* Please review repositioning for modsnap! */
3312+
int is_si = (clnt->dbtran.mode == TRANLEVEL_SNAPISOL || clnt->dbtran.mode == TRANLEVEL_SERIAL);
3313+
if (is_si && !gbl_sql_release_locks_on_si_lockwait)
3314+
return 0;
3315+
rlocks_reason_t reason = is_si ? RLOCKS_REASON_SI_LOCKWAIT : RLOCKS_REASON_LOCKWAIT;
32613316
if (bdb_curtran_has_waiters(thedb->bdb_env, clnt->dbtran.cursor_tran)) {
3262-
rc = release_locks("replication is waiting on si-session");
3317+
rc = release_locks(reason);
32633318
release_locks_on_si_lockwait_cnt++;
32643319
} else if (gbl_sql_random_release_interval &&
32653320
!(rand() % gbl_sql_random_release_interval)) {
3266-
rc = release_locks("random release cursor_move_postop");
3321+
rc = release_locks(RLOCKS_REASON_RANDOM);
32673322
release_locks_on_si_lockwait_cnt++;
32683323
}
32693324
}
@@ -5948,6 +6003,17 @@ int sqlite3BtreeMovetoUnpacked(BtCursor *pCur, /* The cursor to be moved */
59486003
}
59496004
}
59506005

6006+
/* If the cursor was pre-synced by sync_index_data_cursors before a
6007+
* lock release, the cursor is already positioned at the requested
6008+
* genid and the data is in the cursor's buffers. Skip the BDB seek
6009+
* to avoid a "Dta lookup lost the race" failure if the row was deleted
6010+
* during the lock-release window. */
6011+
if (gbl_recover_deadlock_sync_dta && bias == OP_DeferredSeek && pCur->genid == (unsigned long long)intKey &&
6012+
!pCur->eof) {
6013+
*pRes = 0;
6014+
goto done;
6015+
}
6016+
59516017
/* TODO: we already found the data record. find some way to map between
59526018
* index/data cursors and don't do extra data fetches unless we
59536019
* move the cursor */
@@ -11612,6 +11678,14 @@ const char *comdb2_get_sql(void)
1161211678
}
1161311679

1161411680
int gbl_fdb_track_hints = 0;
11681+
static void sqlite3BtreeCursorHint_TableCursor(BtCursor *pCur, BtCursor *pTableCsr)
11682+
{
11683+
assert(pCur->cursor_class == CURSORCLASS_INDEX);
11684+
assert(pTableCsr->cursor_class == CURSORCLASS_TABLE);
11685+
assert(pCur->db == pTableCsr->db);
11686+
pCur->pCursorHintTableCursor = pTableCsr;
11687+
}
11688+
1161511689
static void sqlite3BtreeCursorHint_Range(BtCursor *pCur, const Expr *pExpr)
1161611690
{
1161711691
char *expr = "?no vdbe engine?";
@@ -11667,10 +11741,20 @@ void sqlite3BtreeCursorHint(BtCursor *pCur, int eHintType, ...)
1166711741

1166811742
break;
1166911743
}
11744+
11745+
case BTREE_HINT_TABLECURSOR: {
11746+
sqlite3BtreeCursorHint_TableCursor(pCur, va_arg(ap, BtCursor *));
11747+
break;
11748+
}
1167011749
}
1167111750
va_end(ap);
1167211751
}
1167311752

11753+
BtCursor *sqlite3BtreeCursorHintTblCsr(BtCursor *pCsr)
11754+
{
11755+
return pCsr->pCursorHintTableCursor;
11756+
}
11757+
1167411758
int fdb_packedsqlite_extract_genid(char *key, int *outlen, char *outbuf)
1167511759
{
1167611760
int hdroffset = 0;

db/sqlinterfaces.c

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2827,18 +2827,43 @@ static int check_thd_gen(struct sqlthdstate *thd, struct sqlclntstate *clnt, int
28272827
return SQLITE_OK;
28282828
}
28292829

2830-
int release_locks_int(const char *trace, const char *func, int line, struct sqlclntstate *clnt)
2830+
static const char *rlocks_reason_str(rlocks_reason_t reason)
2831+
{
2832+
switch (reason) {
2833+
case RLOCKS_REASON_SI_LOCKWAIT:
2834+
return "replication is waiting on si-session";
2835+
case RLOCKS_REASON_LOCKWAIT:
2836+
return "replication is waiting on session";
2837+
case RLOCKS_REASON_RANDOM:
2838+
return "random release";
2839+
case RLOCKS_REASON_LOCK_DESIRED:
2840+
return "release locks on emit-row for lock-desired";
2841+
case RLOCKS_REASON_EMIT_ROW:
2842+
return "release locks on emit-row";
2843+
case RLOCKS_REASON_LONG_REPWAIT:
2844+
return "long repwait at emit-row";
2845+
case RLOCKS_REASON_SLOW_READER:
2846+
return "slow reader";
2847+
default:
2848+
return "unknown";
2849+
}
2850+
}
2851+
2852+
int release_locks_int(rlocks_reason_t reason, const char *func, int line, struct sqlclntstate *clnt)
28312853
{
2854+
struct sql_thread *thd = pthread_getspecific(query_info_key);
28322855
if (!clnt) {
2833-
struct sql_thread *thd = pthread_getspecific(query_info_key);
28342856
if (thd) clnt = thd->clnt;
28352857
}
28362858
if (!clnt || !clnt->dbtran.cursor_tran) return -1;
28372859
extern int gbl_sql_release_locks_trace;
28382860
if (gbl_sql_release_locks_trace) {
28392861
logmsg(LOGMSG_USER, "Releasing locks for lockid %d, %s\n",
2840-
bdb_get_lid_from_cursortran(clnt->dbtran.cursor_tran), trace);
2862+
bdb_get_lid_from_cursortran(clnt->dbtran.cursor_tran), rlocks_reason_str(reason));
28412863
}
2864+
extern int gbl_recover_deadlock_sync_dta;
2865+
if (reason == RLOCKS_REASON_LOCKWAIT && gbl_recover_deadlock_sync_dta && thd)
2866+
sync_index_data_cursors(thd);
28422867
return recover_deadlock_flags(thedb->bdb_env, clnt, NULL, -1, func, line, 0);
28432868
}
28442869

@@ -2855,7 +2880,7 @@ int release_locks_on_emit_row(struct sqlclntstate *clnt)
28552880

28562881
/* Always release if we're emitting during a master change */
28572882
if (bdb_lock_desired(thedb->bdb_env))
2858-
return release_locks_int("release locks on emit-row for lock-desired", __func__, __LINE__, clnt);
2883+
return release_locks_int(RLOCKS_REASON_LOCK_DESIRED, __func__, __LINE__, clnt);
28592884

28602885
/* Short circuit if check-waiters or tunable is disabled */
28612886
if (!gbl_locks_check_waiters)
@@ -2867,20 +2892,20 @@ int release_locks_on_emit_row(struct sqlclntstate *clnt)
28672892
/* Release locks randomly for testing */
28682893
if (gbl_sql_random_release_interval &&
28692894
!(rand() % gbl_sql_random_release_interval))
2870-
return release_locks_int("random release emit-row", __func__, __LINE__, clnt);
2895+
return release_locks_int(RLOCKS_REASON_RANDOM, __func__, __LINE__, clnt);
28712896

28722897
/* Short circuit if we don't have any waiters */
28732898
if (!bdb_curtran_has_waiters(thedb->bdb_env, clnt->dbtran.cursor_tran))
28742899
return 0;
28752900

28762901
/* We're emitting a row & have waiters */
28772902
if (!gbl_rep_wait_release_ms || thedb->master == gbl_myhostname)
2878-
return release_locks_int("release locks on emit-row", __func__, __LINE__, clnt);
2903+
return release_locks_int(RLOCKS_REASON_EMIT_ROW, __func__, __LINE__, clnt);
28792904

28802905
/* We're emitting a row and are blocking replication */
28812906
if (rep_lock_time_ms &&
28822907
(comdb2_time_epochms() - rep_lock_time_ms) > gbl_rep_wait_release_ms)
2883-
return release_locks_int("long repwait at emit-row", __func__, __LINE__, clnt);
2908+
return release_locks_int(RLOCKS_REASON_LONG_REPWAIT, __func__, __LINE__, clnt);
28842909

28852910
return 0;
28862911
}
@@ -5687,7 +5712,7 @@ static int recover_deadlock_sbuf(struct sqlclntstate *clnt)
56875712

56885713
/* Sql thread */
56895714
if (thd) {
5690-
if (release_locks_int("slow reader", __func__, __LINE__, clnt) != 0) {
5715+
if (release_locks_int(RLOCKS_REASON_SLOW_READER, __func__, __LINE__, clnt) != 0) {
56915716
assert(bdb_lockref() == 0);
56925717
logmsg(LOGMSG_ERROR, "%s release_locks failed\n", __func__);
56935718
return 1;

lua/sp.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5692,7 +5692,7 @@ static int l_send_back_row(Lua lua, sqlite3_stmt *stmt, int nargs)
56925692
* air to check if bdb_lock_desired */
56935693
while ((rc = pthread_mutex_trylock(parent->emit_mutex)) == EBUSY) {
56945694
if (bdb_lock_desired(thedb->bdb_env)) {
5695-
rc = release_locks("release locks on emit-row for lock-desired");
5695+
rc = release_locks(RLOCKS_REASON_LOCK_DESIRED);
56965696
if (rc) {
56975697
logmsg(LOGMSG_ERROR, "%s release_locks_on_emit_row %d\n", __func__, rc);
56985698
return rc;

sqlite/src/sqlite_btree.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ int sqlite3BtreeNewDb(Btree *p);
185185
*/
186186
#define BTREE_HINT_FLAGS 1 /* Set flags indicating cursor usage */
187187
#define BTREE_HINT_RANGE 2 /* Range constraints on queries */
188+
#define BTREE_HINT_TABLECURSOR 3 /* Table csr associated with this index csr */
188189

189190
/*
190191
** Values that may be OR'd together to form the second argument to the
@@ -230,6 +231,7 @@ int sqlite3BtreeNewDb(Btree *p);
230231

231232
#ifdef SQLITE_ENABLE_CURSOR_HINTS
232233
void sqlite3BtreeCursorHint(BtCursor*, int, ...);
234+
BtCursor *sqlite3BtreeCursorHintTblCsr(BtCursor*);
233235
#endif
234236

235237
#define BTREE_CUR_RD 0x00000001

sqlite/src/vdbe.c

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6563,6 +6563,9 @@ case OP_IdxRowid: { /* out2 */
65636563
assert( pTabCur->eCurType==CURTYPE_BTREE );
65646564
assert( pTabCur->uc.pCursor!=0 );
65656565
assert( pTabCur->isTable );
6566+
assert(
6567+
sqlite3BtreeCursorHintTblCsr(pC->uc.pCursor)==pTabCur->uc.pCursor
6568+
);
65666569
pTabCur->nullRow = 0;
65676570
pTabCur->movetoTarget = rowid;
65686571
pTabCur->deferredMoveto = 1;
@@ -8661,25 +8664,39 @@ case OP_Init: { /* jump */
86618664
}
86628665

86638666
#ifdef SQLITE_ENABLE_CURSOR_HINTS
8664-
/* Opcode: CursorHint P1 * * P4 *
8667+
/* Opcode: CursorHint P1 * P3 P4 *
8668+
**
8669+
** Provide a hint to cursor P1.
86658670
**
8666-
** Provide a hint to cursor P1 that it only needs to return rows that
8667-
** satisfy the Expr in P4. TK_REGISTER terms in the P4 expression refer
8668-
** to values currently held in registers. TK_COLUMN terms in the P4
8671+
** If P4 is of type P4_EXPR, then the hint is that the cursor need only return
8672+
** rows that satisfy the Expr in P4. TK_REGISTER terms in the P4 expression
8673+
** refer to values currently held in registers. TK_COLUMN terms in the P4
86698674
** expression refer to columns in the b-tree to which cursor P1 is pointing.
8675+
** P3 is ignored in this case.
8676+
**
8677+
** Or, if P4 is P4_NOTUSED, then the hint is that cursor P1 is an index cursor
8678+
** used to drive table cursor P3. In other words, that this VM may execute
8679+
** OP_DeferredSeek instructions to lazily position P3 based on current
8680+
** position of P1.
86708681
*/
86718682
case OP_CursorHint: {
86728683
VdbeCursor *pC;
8684+
pC = p->apCsr[pOp->p1];
86738685

86748686
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
8675-
assert( pOp->p4type==P4_EXPR );
8676-
pC = p->apCsr[pOp->p1];
8687+
86778688
if( pC ){
86788689
#if !defined(SQLITE_BUILDING_FOR_COMDB2)
86798690
assert( pC->eCurType==CURTYPE_BTREE );
86808691
#endif /* !defined(SQLITE_BUILDING_FOR_COMDB2) */
8681-
sqlite3BtreeCursorHint(pC->uc.pCursor, BTREE_HINT_RANGE,
8682-
pOp->p4.pExpr, aMem);
8692+
if( pOp->p4type==P4_EXPR ){
8693+
sqlite3BtreeCursorHint(pC->uc.pCursor, BTREE_HINT_RANGE,
8694+
pOp->p4.pExpr, aMem);
8695+
}else if( p->apCsr[pOp->p3] ){
8696+
sqlite3BtreeCursorHint(
8697+
pC->uc.pCursor, BTREE_HINT_TABLECURSOR, p->apCsr[pOp->p3]->uc.pCursor
8698+
);
8699+
}
86838700
}
86848701
break;
86858702
}

0 commit comments

Comments
 (0)