Beispiel #1
0
 def truncate(self, uri, make_key, keynum1, keynum2):
     if self.trunc_with_remove:
         cursor = self.session.open_cursor(uri)
         err = 0
         for k in range(keynum1, keynum2 + 1):
             cursor.set_key(k)
             try:
                 err = cursor.remove()
             except WiredTigerError as e:
                 if wiredtiger_strerror(WT_ROLLBACK) in str(e):
                     err = WT_ROLLBACK
                 elif wiredtiger_strerror(WT_PREPARE_CONFLICT) in str(e):
                     err = WT_PREPARE_CONFLICT
                 else:
                     raise e
             if err != 0:
                 break
         cursor.close()
     else:
         lo_cursor = self.session.open_cursor(uri)
         hi_cursor = self.session.open_cursor(uri)
         lo_cursor.set_key(make_key(keynum1))
         hi_cursor.set_key(make_key(keynum2))
         try:
             err = self.session.truncate(None, lo_cursor, hi_cursor, None)
         except WiredTigerError as e:
             if wiredtiger_strerror(WT_ROLLBACK) in str(e):
                 err = WT_ROLLBACK
             elif wiredtiger_strerror(WT_PREPARE_CONFLICT) in str(e):
                 err = WT_PREPARE_CONFLICT
             else:
                 raise e
         lo_cursor.close()
         hi_cursor.close()
     return err
Beispiel #2
0
def retry_rollback(self, name, txn_session, code):
    retry_limit = 100
    retries = 0
    completed = False
    saved_exception = None
    while not completed and retries < retry_limit:
        if retries != 0:
            self.pr("Retrying operation for " + name)
            if txn_session:
                txn_session.rollback_transaction()
            sleep(0.1)
            if txn_session:
                txn_session.begin_transaction('isolation=snapshot')
                self.pr("Began new transaction for " + name)
        try:
            code()
            completed = True
        except WiredTigerError as e:
            rollback_str = wiredtiger_strerror(WT_ROLLBACK)
            if rollback_str not in str(e):
                raise(e)
            retries += 1
            saved_exception = e
    if not completed and saved_exception:
        raise(saved_exception)
 def large_updates(self, uri, value, ds, nrows, prepare, commit_ts):
     # Update a large number of records.
     session = self.session
     try:
         cursor = session.open_cursor(uri)
         for i in range(1, nrows + 1):
             session.begin_transaction()
             cursor[ds.key(i)] = value
             if commit_ts == 0:
                 session.commit_transaction()
             elif prepare:
                 session.prepare_transaction('prepare_timestamp=' +
                                             self.timestamp_str(commit_ts -
                                                                1))
                 session.timestamp_transaction(
                     'commit_timestamp=' + self.timestamp_str(commit_ts))
                 session.timestamp_transaction(
                     'durable_timestamp=' +
                     self.timestamp_str(commit_ts + 1))
                 session.commit_transaction()
             else:
                 session.commit_transaction('commit_timestamp=' +
                                            self.timestamp_str(commit_ts))
         cursor.close()
     except WiredTigerError as e:
         rollback_str = wiredtiger_strerror(WT_ROLLBACK)
         if rollback_str in str(e):
             session.rollback_transaction()
         raise (e)
    def large_modifies(self, uri, value, ds, location, nbytes, nrows, prepare, commit_ts):
        # Load a slight modification.
        session = self.session
        try:
            cursor = session.open_cursor(uri)
            session.begin_transaction()
            for i in range(1, nrows + 1):
                cursor.set_key(i)
                mods = [wiredtiger.Modify(value, location, nbytes)]
                self.assertEqual(cursor.modify(mods), 0)

            if commit_ts == 0:
                session.commit_transaction()
            elif prepare:
                session.prepare_transaction('prepare_timestamp=' + self.timestamp_str(commit_ts-1))
                session.timestamp_transaction('commit_timestamp=' + self.timestamp_str(commit_ts))
                session.timestamp_transaction('durable_timestamp=' + self.timestamp_str(commit_ts+1))
                session.commit_transaction()
            else:
                session.commit_transaction('commit_timestamp=' + self.timestamp_str(commit_ts))
            cursor.close()
        except WiredTigerError as e:
            rollback_str = wiredtiger_strerror(WT_ROLLBACK)
            if rollback_str in str(e):
                session.rollback_transaction()
            raise(e)
 def prepare_call(self, func):
     try:
         ret = func()
     except wiredtiger.WiredTigerError as e:
         if wiredtiger.wiredtiger_strerror(wiredtiger.WT_PREPARE_CONFLICT) in str(e):
             ret = wiredtiger.WT_PREPARE_CONFLICT
         else:
             raise e
     return ret
Beispiel #6
0
 def truncate(self, uri, make_key, keynum1, keynum2):
     if self.trunc_with_remove:
         cursor = self.session.open_cursor(uri)
         err = 0
         for k in range(keynum1, keynum2 + 1):
             cursor.set_key(k)
             try:
                 # In this test some or all the data is already deleted; skip those rows.
                 err = cursor.search()
                 if err == wiredtiger.WT_NOTFOUND:
                     err = 0
                 else:
                     err = cursor.remove()
             except wiredtiger.WiredTigerError as e:
                 if wiredtiger.wiredtiger_strerror(
                         wiredtiger.WT_ROLLBACK) in str(e):
                     err = wiredtiger.WT_ROLLBACK
                 else:
                     raise e
             if err != 0:
                 break
         cursor.close()
     else:
         lo_cursor = self.session.open_cursor(uri)
         hi_cursor = self.session.open_cursor(uri)
         lo_cursor.set_key(make_key(keynum1))
         hi_cursor.set_key(make_key(keynum2))
         try:
             err = self.session.truncate(None, lo_cursor, hi_cursor, None)
         except wiredtiger.WiredTigerError as e:
             if wiredtiger.wiredtiger_strerror(
                     wiredtiger.WT_ROLLBACK) in str(e):
                 err = wiredtiger.WT_ROLLBACK
             else:
                 raise e
         lo_cursor.close()
         hi_cursor.close()
     return err
Beispiel #7
0
 def truncate(self, uri, key1, key2):
     if self.trunc_with_remove:
         # Because remove clears the cursor position, removing by cursor-next is a nuisance.
         scan_cursor = self.session.open_cursor(uri)
         del_cursor = self.session.open_cursor(uri)
         err = 0
         scan_cursor.set_key(key1)
         self.assertEqual(scan_cursor.search(), 0)
         while scan_cursor.get_key() <= key2:
             del_cursor.set_key(scan_cursor.get_key())
             try:
                 err = del_cursor.remove()
             except WiredTigerError as e:
                 if wiredtiger_strerror(WT_ROLLBACK) in str(e):
                     err = WT_ROLLBACK
                 elif wiredtiger_strerror(WT_PREPARE_CONFLICT) in str(e):
                     err = WT_PREPARE_CONFLICT
                 else:
                     raise e
             if err != 0:
                 break
             if scan_cursor.get_key() == key2:
                 break
             try:
                 err = scan_cursor.next()
             except WiredTigerError as e:
                 if wiredtiger_strerror(WT_ROLLBACK) in str(e):
                     err = WT_ROLLBACK
                 elif wiredtiger_strerror(WT_PREPARE_CONFLICT) in str(e):
                     err = WT_PREPARE_CONFLICT
                 else:
                     raise e
             if err != 0:
                 break
         scan_cursor.close()
         del_cursor.close()
     else:
         lo_cursor = self.session.open_cursor(uri)
         hi_cursor = self.session.open_cursor(uri)
         lo_cursor.set_key(key1)
         hi_cursor.set_key(key2)
         try:
             err = self.session.truncate(None, lo_cursor, hi_cursor, None)
         except WiredTigerError as e:
             if wiredtiger_strerror(WT_ROLLBACK) in str(e):
                 err = WT_ROLLBACK
             elif wiredtiger_strerror(WT_PREPARE_CONFLICT) in str(e):
                 err = WT_PREPARE_CONFLICT
             else:
                 raise e
         lo_cursor.close()
         hi_cursor.close()
     return err
def retry_rollback(self, name, code):
    retry_limit = 100
    retries = 0
    completed = False
    saved_exception = None
    while not completed and retries < retry_limit:
        if retries != 0:
            self.pr("Retrying operation for " + name)
            sleep(0.1)
        try:
            code()
            completed = True
        except WiredTigerError as e:
            rollback_str = wiredtiger_strerror(WT_ROLLBACK)
            if rollback_str not in str(e):
                raise (e)
            retries += 1
            saved_exception = e
    if not completed and saved_exception:
        raise (saved_exception)
    def large_modifies(self, uri, value, ds, location, nbytes, nrows, prepare,
                       commit_ts):
        # Load a slight modification.
        session = self.session
        try:
            cursor = session.open_cursor(uri)
            session.begin_transaction()
            for i in range(1, nrows + 1):
                cursor.set_key(i)
                # FLCS doesn't support modify (for obvious reasons) so just update.
                # Use the first character of the passed-in value.
                if self.value_format == '8t':
                    cursor.set_value(bytes(value, encoding='utf-8')[0])
                    self.assertEqual(cursor.update(), 0)
                else:
                    mods = [wiredtiger.Modify(value, location, nbytes)]
                    self.assertEqual(cursor.modify(mods), 0)

            if commit_ts == 0:
                session.commit_transaction()
            elif prepare:
                session.prepare_transaction('prepare_timestamp=' +
                                            self.timestamp_str(commit_ts - 1))
                session.timestamp_transaction('commit_timestamp=' +
                                              self.timestamp_str(commit_ts))
                session.timestamp_transaction('durable_timestamp=' +
                                              self.timestamp_str(commit_ts +
                                                                 1))
                session.commit_transaction()
            else:
                session.commit_transaction('commit_timestamp=' +
                                           self.timestamp_str(commit_ts))
            cursor.close()
        except WiredTigerError as e:
            rollback_str = wiredtiger_strerror(WT_ROLLBACK)
            if rollback_str in str(e):
                session.rollback_transaction()
            raise (e)
Beispiel #10
0
    def test_prepare(self):
        self.create()

        session2 = self.conn.open_session()
        cursor = session2.open_cursor(self.uri, None)
        # Add a value to the update chain
        session2.begin_transaction()
        cursor[1] = 0
        session2.prepare_transaction("prepare_timestamp=" +
                                     self.timestamp_str(1))

        evict_cursor = self.session.open_cursor(self.uri, None,
                                                "debug=(release_evict)")
        self.session.begin_transaction()
        evict_cursor.set_key(1)
        try:
            evict_cursor.search()
        except wiredtiger.WiredTigerError as e:
            if wiredtiger.wiredtiger_strerror(
                    wiredtiger.WT_PREPARE_CONFLICT) not in str(e):
                raise e
        evict_cursor.reset()
        self.session.rollback_transaction()

        # Open a version cursor
        self.session.begin_transaction()
        version_cursor = self.session.open_cursor(self.uri, None,
                                                  "debug=(dump_version=true)")
        version_cursor.set_key(1)
        self.assertEquals(version_cursor.search(), 0)
        self.assertEquals(version_cursor.get_key(), 1)
        self.verify_value(version_cursor, 1, 0, WT_TS_MAX, WT_TS_MAX, 3, 1, 4,
                          0, 0)
        self.assertEquals(version_cursor.next(), 0)
        self.assertEquals(version_cursor.get_key(), 1)
        self.verify_value(version_cursor, 1, 1, 1, 0, 3, 1, 0, 1, 0)
        self.assertEquals(version_cursor.next(), wiredtiger.WT_NOTFOUND)
Beispiel #11
0
    def test_timestamp(self):

        # Create a file that contains active history (content newer than the oldest timestamp).
        table_uri = 'table:timestamp23'
        ds = SimpleDataSet(self,
                           table_uri,
                           0,
                           key_format=self.key_format,
                           value_format='S',
                           config='log=(enabled=false)')
        ds.populate()
        self.session.checkpoint()

        key = 5
        value_1 = 'a' * 500
        value_2 = 'b' * 500
        value_3 = 'c' * 500

        # Pin oldest and stable to timestamp 1.
        self.conn.set_timestamp('oldest_timestamp=' + self.timestamp_str(1) +
                                ',stable_timestamp=' + self.timestamp_str(1))

        cursor = self.session.open_cursor(ds.uri)

        # Write two values at timestamp 10. We'll muck with the first value
        # and use the second to reference the page for eviction.
        self.session.begin_transaction('read_timestamp=10')
        cursor[key] = value_1
        cursor[key + 1] = value_2
        self.session.commit_transaction('commit_timestamp=11')

        # Delete the first value at timestamp 20.
        self.session.begin_transaction('read_timestamp=20')
        cursor.set_key(key)
        cursor.remove()
        self.session.commit_transaction('commit_timestamp=21')

        # Put it back at timestamp 30.
        self.session.begin_transaction('read_timestamp=30')
        cursor[key] = value_3
        self.session.commit_transaction('commit_timestamp=31')

        # Delete it again at timestamp 40.
        self.session.begin_transaction('read_timestamp=40')
        cursor.set_key(key)
        cursor.remove()
        self.session.commit_transaction('commit_timestamp=41')

        # Evict the page using the second key.
        evict_cursor = self.session.open_cursor(ds.uri, None,
                                                "debug=(release_evict)")
        self.session.begin_transaction()
        v = evict_cursor[key + 1]
        self.assertEqual(v, value_2)
        self.assertEqual(evict_cursor.reset(), 0)
        self.session.rollback_transaction()

        # Create a separate session and a cursor to read the original value at timestamp 12.
        session2 = self.conn.open_session()
        cursor2 = session2.open_cursor(ds.uri)
        session2.begin_transaction('read_timestamp=12')
        v = cursor2[key]
        self.assertEqual(v, value_1)

        self.session.breakpoint()

        # Now delete the original value. This _should_ cause WT_ROLLBACK, but with a column
        # store bug seen and fixed in August 2021, it succeeds, and the resulting invalid
        # tombstone will cause reconciliation to assert. (To see this behavior, comment out the
        # self.fail call and let the transaction commit.)
        try:
            cursor2.remove()
            self.fail("Conflicting remove did not fail")
            session2.commit_transaction('commit_timestamp=50')
        except wiredtiger.WiredTigerError as e:
            self.assertTrue(
                wiredtiger.wiredtiger_strerror(wiredtiger.WT_ROLLBACK) in str(
                    e))

        cursor.close()
        cursor2.close()
Beispiel #12
0
    def test_timestamp(self):

        table_uri = 'table:timestamp24'
        ds = SimpleDataSet(self,
                           table_uri,
                           0,
                           key_format=self.key_format,
                           value_format=self.value_format)
        ds.populate()
        self.session.checkpoint()

        key = 5
        if self.value_format == '8t':
            value_a = 97
            value_b = 98
            value_c = 99
            value_d = 100
        else:
            value_a = 'a' * 500
            value_b = 'b' * 500
            value_c = 'c' * 500
            value_d = 'd' * 500

        # Pin oldest and stable to timestamp 1.
        #self.conn.set_timestamp('oldest_timestamp=' + self.timestamp_str(1) +
        #    ',stable_timestamp=' + self.timestamp_str(1))

        # Create two sessions so we can have two transactions.
        session1 = self.session
        session2 = self.conn.open_session()

        # In session 1, write value_a at time 20.
        # Commit that and then start a new transaction. Read the value back.
        # Then reset the cursor so the page isn't pinned, but leave the transaction open.
        cursor1 = session1.open_cursor(ds.uri)
        session1.begin_transaction()
        cursor1[key] = value_a
        session1.commit_transaction('commit_timestamp=20')
        session1.begin_transaction('read_timestamp=25')
        tmp = cursor1[key]
        self.assertEqual(tmp, value_a)
        cursor1.reset()
        # leave session1's transaction open

        # In session 2, write value_b at time 50. Commit that.
        cursor2 = session2.open_cursor(ds.uri)
        session2.begin_transaction()
        cursor2[key] = value_b
        session2.commit_transaction('commit_timestamp=50')
        cursor2.reset()

        # Evict the page to force reconciliation. value_b goes to disk; value_a to history.
        # Use session2 so we can keep session1's transaction open.
        self.evict(ds.uri, session2, key, value_b)

        # In session 2, write value_c, but abort it.
        session2.begin_transaction()
        cursor2[key] = value_c
        session2.rollback_transaction()

        # Now in session 1 try to write value_d. This should produce WT_ROLLBACK, but with
        # a bug seen and fixed in August 2021, succeeds improperly instead, resulting in
        # data corruption. The behavior is more exciting when the update is a modify (the
        # modify gets applied to value_b instead of value_a, producing a more detectable
        # corruption) but this is not necessary to check the wrong behavior.

        try:
            cursor1[key] = value_d
            self.fail("Conflicting update did not fail")
            broken = True
        except wiredtiger.WiredTigerError as e:
            self.assertTrue(
                wiredtiger.wiredtiger_strerror(wiredtiger.WT_ROLLBACK) in str(
                    e))
            broken = False

        # Put this outside the try block in case it throws its own exceptions
        if broken:
            session1.commit_transaction('commit_timestamp=30')
        else:
            session1.rollback_transaction()

        # Read the data back
        session2.begin_transaction('read_timestamp=60')
        tmp = cursor2[key]

        # It should be value_b. But if we broke, it'll be value_d.
        self.assertEqual(tmp, value_d if broken else value_b)

        cursor2.close()
        cursor1.close()
Beispiel #13
0
    def test_truncate15(self):
        # Note: 50000 is not large enough to trigger the problem.
        nrows = 100000

        uri = "table:truncate15"
        ds = SimpleDataSet(self,
                           uri,
                           0,
                           key_format=self.key_format,
                           value_format=self.value_format,
                           config='log=(enabled=false)' + self.extraconfig)
        ds.populate()

        if self.value_format == '8t':
            value_a = 97
            value_b = 98
        else:
            value_a = "aaaaa" * 500
            value_b = "bbbbb" * 500

        # Pin oldest and stable timestamps to 1.
        self.conn.set_timestamp('oldest_timestamp=' + self.timestamp_str(1) +
                                ',stable_timestamp=' + self.timestamp_str(1))

        # Write a bunch of data at time 10.
        cursor = self.session.open_cursor(ds.uri)
        self.session.begin_transaction()
        for i in range(1, nrows + 1):
            cursor[ds.key(i)] = value_a
            # Commit every 101 rows to avoid overflowing the cache.
            if i % 101 == 0:
                self.session.commit_transaction('commit_timestamp=' +
                                                self.timestamp_str(10))
                self.session.begin_transaction()
        self.session.commit_transaction('commit_timestamp=' +
                                        self.timestamp_str(10))

        # Mark it stable.
        self.conn.set_timestamp('stable_timestamp=' + self.timestamp_str(10))

        # Reopen the connection so nothing is in memory and we can fast-truncate.
        self.reopen_conn()

        # Truncate the data at time 25, but prepare at 20 and make durable 30.
        self.session.begin_transaction()
        err = self.truncate(ds.uri, ds.key, nrows // 4 + 1,
                            nrows // 4 + nrows // 2)
        self.assertEqual(err, 0)
        self.session.prepare_transaction('prepare_timestamp=' +
                                         self.timestamp_str(20))
        self.session.timestamp_transaction('commit_timestamp=' +
                                           self.timestamp_str(25))
        self.session.commit_transaction('durable_timestamp=' +
                                        self.timestamp_str(30))

        # Make sure we did at least one fast-delete. For FLCS, there's no fast-delete
        # support, so assert we didn't.
        stat_cursor = self.session.open_cursor('statistics:', None, None)
        fastdelete_pages = stat_cursor[stat.conn.rec_page_delete_fast][2]
        if self.value_format == '8t':
            self.assertEqual(fastdelete_pages, 0)
        else:
            self.assertGreater(fastdelete_pages, 0)

        # Advance stable.
        self.conn.set_timestamp('stable_timestamp=' + self.timestamp_str(30))
        self.session.checkpoint()

        # Reopen the connection so nothing is in memory.
        self.reopen_conn(
            ".",
            "cache_size=1MB,eviction_dirty_target=90,eviction_dirty_trigger=100"
            +
            ",eviction_updates_target=90,eviction_updates_trigger=100,readonly=true"
        )

        # Validate the data.
        try:
            # At time 10 we should see all value_a.
            self.check(ds.uri, ds.key, nrows, 0, value_a, 10)
            #self.evict_cursor(ds.uri, ds, nrows, 10)

            # At time 20 we should still see all value_a.
            self.check(ds.uri, ds.key, nrows, 0, value_a, 20)
            #self.evict_cursor(ds.uri, ds, nrows, 20)

            # At time 25 we should still see half value_a, and for FLCS, half zeros.
            self.check(ds.uri, ds.key, nrows // 2, nrows // 2, value_a, 25)
            #self.evict_cursor(ds.uri, ds, nrows // 2, 25)

            # At time 30 we should also see half value_a, and for FLCS, half zeros.
            self.check(ds.uri, ds.key, nrows // 2, nrows // 2, value_a, 30)
            #self.evict_cursor(ds.uri, ds, nrows // 2, 30)
        except WiredTigerError as e:
            # If we get WT_ROLLBACK while reading, assume it's because we overflowed the
            # cache, and fail. (If we don't trap this explicitly, the test harness retries
            # the whole test, which is not what we want.)
            if wiredtiger_strerror(WT_ROLLBACK) in str(e):
                self.assertTrue(False)
            else:
                raise e