@@ -3246,6 +3246,54 @@ static inline int sqlite3VdbeCompareRecordPacked(KeyInfo *pKeyInfo, int k1len,
32463246}
32473247
32483248unsigned 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 */
32503298static 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
1161411680int 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+
1161511689static 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+
1167411758int fdb_packedsqlite_extract_genid (char * key , int * outlen , char * outbuf )
1167511759{
1167611760 int hdroffset = 0 ;
0 commit comments