List:Commits« Previous MessageNext Message »
From:Martin Skold Date:September 7 2009 9:31am
Subject:bzr commit into mysql-5.1-telco-6.2 branch (Martin.Skold:2973) Bug#38034
Bug#46712 Bug#47039 Bug#47072
View as plain text  
#At file:///home/marty/MySQL/mysql-5.1-telco-6.2_orig/

 2973 Martin Skold	2009-09-07 [merge]
      Merge
      modified:
        mysql-test/suite/ndb/r/ndb_auto_increment.result
        mysql-test/suite/ndb/r/ndb_trigger.result
        mysql-test/suite/ndb/t/ndb_auto_increment.test
        mysql-test/suite/ndb/t/ndb_trigger.test
        sql/ha_ndbcluster.cc
        storage/ndb/include/kernel/signaldata/CloseComReqConf.hpp
        storage/ndb/src/kernel/blocks/ERROR_codes.txt
        storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp
        storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp
        storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp
        storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp
        storage/ndb/src/kernel/blocks/dbtup/DbtupDiskAlloc.cpp
        storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp
        storage/ndb/src/kernel/blocks/qmgr/Qmgr.hpp
        storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp
        storage/ndb/test/ndbapi/testNdbApi.cpp
        storage/ndb/test/run-test/autotest-boot.sh
        storage/ndb/test/run-test/daily-basic-tests.txt

=== modified file 'mysql-test/suite/ndb/r/ndb_auto_increment.result'
--- a/mysql-test/suite/ndb/r/ndb_auto_increment.result	2008-01-25 09:43:30 +0000
+++ b/mysql-test/suite/ndb/r/ndb_auto_increment.result	2009-09-03 07:49:50 +0000
@@ -3,6 +3,9 @@ DROP TABLE IF EXISTS t1;
 set @old_auto_increment_offset = @@session.auto_increment_offset;
 set @old_auto_increment_increment = @@session.auto_increment_increment;
 set @old_ndb_autoincrement_prefetch_sz = @@session.ndb_autoincrement_prefetch_sz;
+set @old_auto_increment_offset = @@session.auto_increment_offset;
+set @old_auto_increment_increment = @@session.auto_increment_increment;
+set @old_ndb_autoincrement_prefetch_sz = @@session.ndb_autoincrement_prefetch_sz;
 flush status;
 create table t1 (a int not null auto_increment primary key) engine ndb;
 insert into t1 values (NULL);
@@ -443,3 +446,56 @@ set auto_increment_offset = @old_auto_in
 set auto_increment_increment = @old_auto_increment_increment;
 set ndb_autoincrement_prefetch_sz = @old_ndb_autoincrement_prefetch_sz;
 drop table t1;
+set auto_increment_offset = @old_auto_increment_offset;
+set auto_increment_increment = @old_auto_increment_increment;
+set ndb_autoincrement_prefetch_sz = @old_ndb_autoincrement_prefetch_sz;
+CREATE TABLE `t1` (
+`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+`k` int(10) unsigned NOT NULL DEFAULT '0',
+`c` char(120) NOT NULL DEFAULT '',
+`pad` char(60) NOT NULL DEFAULT '',
+PRIMARY KEY (`id`),
+KEY `k` (`k`)
+) ENGINE=ndbcluster;
+CREATE TABLE `t2` (
+`evend_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+`timestamp` int(11) NOT NULL,
+`server_id` int(11) NOT NULL,
+PRIMARY KEY (`evend_id`)
+) ENGINE=ndbcluster;
+insert into t1 values (null,1,'',''),(null,2,'','');
+CREATE TRIGGER tr1
+AFTER UPDATE ON t1
+FOR EACH ROW
+BEGIN
+insert into t2(timestamp, server_id) values(UNIX_TIMESTAMP(),@@global.server_id);
+end;
+|
+CREATE TRIGGER tr1
+AFTER UPDATE ON t1
+FOR EACH ROW
+BEGIN
+insert into t2(timestamp, server_id) values(UNIX_TIMESTAMP(),@@global.server_id);
+end;
+|
+update t1 set c='foobar' where id=1;
+update t1 set c='foobar' where id=1;
+update t1 set c='foobar' where id=1;
+update t1 set c='foobar' where id=1;
+update t1 set c='foobar' where id=1;
+update t1 set c='foobar' where id=1;
+update t1 set c='foobar' where id=1;
+update t1 set c='foobar' where id=1;
+select evend_id,server_id from t2 order by evend_id;
+evend_id	server_id
+1	1
+2	2
+3	1
+4	2
+5	1
+6	2
+7	1
+8	2
+drop trigger tr1;
+drop table t1, t2;
+drop trigger if exists tr1;

=== modified file 'mysql-test/suite/ndb/r/ndb_trigger.result'
--- a/mysql-test/suite/ndb/r/ndb_trigger.result	2009-02-01 21:05:19 +0000
+++ b/mysql-test/suite/ndb/r/ndb_trigger.result	2009-09-03 07:50:51 +0000
@@ -340,4 +340,68 @@ trigger_name='trg1';
 trigger_name	event_object_table
 trg1	t2
 drop table t2;
+create table t1(c1 int(11) not null auto_increment primary key, c2 int(11) not null) engine=ndb;
+create trigger bi_t1 before insert on t1
+FOR EACH ROW BEGIN 
+SET new.c2 = last_insert_id();
+End
+|
+INSERT INTO t1 (c2) VALUES (17);
+INSERT INTO t1 (c2) VALUES (17);
+INSERT INTO t1 (c2) VALUES (17);
+select c1 from t1 where c1=last_insert_id();
+c1
+3
+select * from t1 order by c1;
+c1	c2
+1	0
+2	1
+3	2
+start transaction;
+INSERT INTO t1 (c2) VALUES (17);
+INSERT INTO t1 (c2) VALUES (17);
+select c1 from t1 where c1=last_insert_id();
+c1
+5
+select * from t1 order by c1;
+c1	c2
+1	0
+2	1
+3	2
+4	3
+5	4
+rollback;
+select c1 from t1 where c1=last_insert_id();
+c1
+select * from t1 order by c1;
+c1	c2
+1	0
+2	1
+3	2
+start transaction;
+INSERT INTO t1 (c2) VALUES (17);
+INSERT INTO t1 (c2) VALUES (17);
+select c1 from t1 where c1=last_insert_id();
+c1
+7
+select * from t1 order by c1;
+c1	c2
+1	0
+2	1
+3	2
+6	5
+7	6
+commit;
+select c1 from t1 where c1=last_insert_id();
+c1
+7
+select * from t1 order by c1;
+c1	c2
+1	0
+2	1
+3	2
+6	5
+7	6
+drop trigger bi_t1;
+drop table t1;
 End of 5.1 tests

=== modified file 'mysql-test/suite/ndb/t/ndb_auto_increment.test'
--- a/mysql-test/suite/ndb/t/ndb_auto_increment.test	2007-11-06 09:57:49 +0000
+++ b/mysql-test/suite/ndb/t/ndb_auto_increment.test	2009-09-03 07:49:50 +0000
@@ -6,6 +6,9 @@ connection server1;
 DROP TABLE IF EXISTS t1,t2;
 connection server2;
 DROP TABLE IF EXISTS t1;
+set @old_auto_increment_offset = @@session.auto_increment_offset;
+set @old_auto_increment_increment = @@session.auto_increment_increment;
+set @old_ndb_autoincrement_prefetch_sz = @@session.ndb_autoincrement_prefetch_sz;
 connection server1;
 --enable_warnings
 
@@ -291,3 +294,87 @@ set auto_increment_increment = @old_auto
 set ndb_autoincrement_prefetch_sz = @old_ndb_autoincrement_prefetch_sz;
 
 drop table t1;
+
+connection server2;
+set auto_increment_offset = @old_auto_increment_offset;
+set auto_increment_increment = @old_auto_increment_increment;
+set ndb_autoincrement_prefetch_sz = @old_ndb_autoincrement_prefetch_sz;
+
+# bug#46712 Auto_increment work incorrectly when using triggers and NDB Cluster
+#
+# Testing that auto_increment values are set correctly when inserting from
+# multiple SQL-nodes
+
+connection server1;
+
+CREATE TABLE `t1` (
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `k` int(10) unsigned NOT NULL DEFAULT '0',
+ `c` char(120) NOT NULL DEFAULT '',
+ `pad` char(60) NOT NULL DEFAULT '',
+ PRIMARY KEY (`id`),
+ KEY `k` (`k`)
+) ENGINE=ndbcluster;
+
+CREATE TABLE `t2` (
+  `evend_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `timestamp` int(11) NOT NULL,
+  `server_id` int(11) NOT NULL,
+   PRIMARY KEY (`evend_id`)
+) ENGINE=ndbcluster;
+
+
+insert into t1 values (null,1,'',''),(null,2,'','');
+
+DELIMITER |;
+
+CREATE TRIGGER tr1
+  AFTER UPDATE ON t1
+  FOR EACH ROW
+  BEGIN
+    insert into t2(timestamp, server_id) values(UNIX_TIMESTAMP(),@@global.server_id);
+  end;
+|
+
+DELIMITER ;|
+
+connection server2;
+
+DELIMITER |;
+
+CREATE TRIGGER tr1
+  AFTER UPDATE ON t1
+  FOR EACH ROW
+  BEGIN
+    insert into t2(timestamp, server_id) values(UNIX_TIMESTAMP(),@@global.server_id);
+  end;
+|
+
+DELIMITER ;|
+
+connection server1;
+update t1 set c='foobar' where id=1;
+connection server2;
+update t1 set c='foobar' where id=1;
+connection server1;
+update t1 set c='foobar' where id=1;
+connection server2;
+update t1 set c='foobar' where id=1;
+connection server1;
+update t1 set c='foobar' where id=1;
+connection server2;
+update t1 set c='foobar' where id=1;
+connection server1;
+update t1 set c='foobar' where id=1;
+connection server2;
+update t1 set c='foobar' where id=1;
+connection server1;
+select evend_id,server_id from t2 order by evend_id;
+
+drop trigger tr1;
+drop table t1, t2;
+
+connection server2;
+--disable_warnings
+drop trigger if exists tr1;
+--enable_warnings

=== modified file 'mysql-test/suite/ndb/t/ndb_trigger.test'
--- a/mysql-test/suite/ndb/t/ndb_trigger.test	2009-02-01 21:05:19 +0000
+++ b/mysql-test/suite/ndb/t/ndb_trigger.test	2009-09-03 07:50:51 +0000
@@ -258,4 +258,45 @@ trigger_name='trg1';
 connection server1;
 drop table t2;
 
+# bug#38034: last_insert_id() value not shown in table with trigger
+#
+# testing that last_insert_id is correct in ongoing transaction.
+
+create table t1(c1 int(11) not null auto_increment primary key, c2 int(11) not null) engine=ndb;
+
+delimiter |;
+
+create trigger bi_t1 before insert on t1
+FOR EACH ROW BEGIN 
+   SET new.c2 = last_insert_id();
+End
+|
+
+delimiter ;|
+
+
+INSERT INTO t1 (c2) VALUES (17);
+INSERT INTO t1 (c2) VALUES (17);
+INSERT INTO t1 (c2) VALUES (17);
+select c1 from t1 where c1=last_insert_id();
+select * from t1 order by c1;
+start transaction;
+INSERT INTO t1 (c2) VALUES (17);
+INSERT INTO t1 (c2) VALUES (17);
+select c1 from t1 where c1=last_insert_id();
+select * from t1 order by c1;
+rollback;
+select c1 from t1 where c1=last_insert_id();
+select * from t1 order by c1;
+start transaction;
+INSERT INTO t1 (c2) VALUES (17);
+INSERT INTO t1 (c2) VALUES (17);
+select c1 from t1 where c1=last_insert_id();
+select * from t1 order by c1;
+commit;
+select c1 from t1 where c1=last_insert_id();
+select * from t1 order by c1;
+drop trigger bi_t1;
+drop table t1;
+
 --echo End of 5.1 tests

=== modified file 'sql/ha_ndbcluster.cc'
--- a/sql/ha_ndbcluster.cc	2009-09-07 08:53:55 +0000
+++ b/sql/ha_ndbcluster.cc	2009-09-07 09:31:01 +0000
@@ -4243,6 +4243,8 @@ int ha_ndbcluster::reset()
   m_ignore_dup_key= FALSE;
   m_use_write= FALSE;
   m_ignore_no_key= FALSE;
+  m_rows_inserted= (ha_rows) 0;
+  m_rows_to_insert= (ha_rows) 1;
   m_delete_cannot_batch= FALSE;
   m_update_cannot_batch= FALSE;
 
@@ -4312,10 +4314,18 @@ void ha_ndbcluster::start_bulk_insert(ha
   if (rows == (ha_rows) 0)
   {
     /* We don't know how many will be inserted, guess */
-    m_rows_to_insert= m_autoincrement_prefetch;
+    m_rows_to_insert=
+      (m_autoincrement_prefetch > NDB_DEFAULT_AUTO_PREFETCH)
+      ? m_autoincrement_prefetch
+      : NDB_DEFAULT_AUTO_PREFETCH;
+    m_autoincrement_prefetch= m_rows_to_insert;
   }
   else
-    m_rows_to_insert= rows; 
+  {
+    m_rows_to_insert= rows;
+    if (m_autoincrement_prefetch < m_rows_to_insert)
+      m_autoincrement_prefetch= m_rows_to_insert;
+  }
 
   DBUG_VOID_RETURN;
 }
@@ -4593,11 +4603,7 @@ int ha_ndbcluster::init_handler_for_stat
   DBUG_ENTER("ha_ndbcluster::init_handler_for_statement");
   // store thread specific data first to set the right context
   m_force_send=          thd->variables.ndb_force_send;
-  m_autoincrement_prefetch=
-    (thd->variables.ndb_autoincrement_prefetch_sz > 
-     NDB_DEFAULT_AUTO_PREFETCH) ?
-    (ha_rows) thd->variables.ndb_autoincrement_prefetch_sz
-    : (ha_rows) NDB_DEFAULT_AUTO_PREFETCH;
+  m_autoincrement_prefetch= thd->variables.ndb_autoincrement_prefetch_sz;
   m_thd_ndb= thd_ndb;
   DBUG_ASSERT(m_thd_ndb->trans);
   // Start of transaction
@@ -6543,26 +6549,11 @@ void ha_ndbcluster::get_auto_increment(u
                                        ulonglong *first_value,
                                        ulonglong *nb_reserved_values)
 {
-  uint cache_size;
   Uint64 auto_value;
   THD *thd= current_thd;
   DBUG_ENTER("get_auto_increment");
   DBUG_PRINT("enter", ("m_tabname: %s", m_tabname));
   Ndb *ndb= get_ndb(table->in_use);
-   
-  if (m_rows_inserted > m_rows_to_insert)
-  {
-    /* We guessed too low */
-    m_rows_to_insert+= m_autoincrement_prefetch;
-  }
-  uint remaining= m_rows_to_insert - m_rows_inserted;
-  uint min_prefetch= 
-    (remaining < thd->variables.ndb_autoincrement_prefetch_sz) ?
-    thd->variables.ndb_autoincrement_prefetch_sz
-    : remaining;
-  cache_size= ((remaining < m_autoincrement_prefetch) ?
-	       min_prefetch
-	       : remaining);
   uint retries= NDB_AUTO_INCREMENT_RETRIES;
   int retry_sleep= 30; /* 30 milliseconds, transaction */
   for (;;)
@@ -6570,7 +6561,9 @@ void ha_ndbcluster::get_auto_increment(u
     Ndb_tuple_id_range_guard g(m_share);
     if (m_skip_auto_increment &&
         ndb->readAutoIncrementValue(m_table, g.range, auto_value) ||
-        ndb->getAutoIncrementValue(m_table, g.range, auto_value, cache_size, increment, offset))
+        ndb->getAutoIncrementValue(m_table, g.range, auto_value, 
+                                   Uint32(m_autoincrement_prefetch), 
+                                   increment, offset))
     {
       if (--retries && !thd->killed &&
           ndb->getNdbError().status == NdbError::TemporaryError)

=== modified file 'storage/ndb/include/kernel/signaldata/CloseComReqConf.hpp'
--- a/storage/ndb/include/kernel/signaldata/CloseComReqConf.hpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/include/kernel/signaldata/CloseComReqConf.hpp	2009-09-01 10:50:11 +0000
@@ -42,10 +42,17 @@ class CloseComReqConf {
   friend bool printCLOSECOMREQCONF(FILE * output, const Uint32 * theData, Uint32 len, Uint16 receiverBlockNo);
   
 public:
-  STATIC_CONST( SignalLength = 3 + NodeBitmask::Size );
+  STATIC_CONST( SignalLength = 4 + NodeBitmask::Size );
 private:
   
+  enum RequestType {
+    RT_API_FAILURE   = 0,
+    RT_NODE_FAILURE  = 1,
+    RT_NO_REPLY      = 2
+  };
+
   Uint32 xxxBlockRef;
+  Uint32 requestType;
   Uint32 failNo;
   
   Uint32 noOfNodes;

=== modified file 'storage/ndb/src/kernel/blocks/ERROR_codes.txt'
--- a/storage/ndb/src/kernel/blocks/ERROR_codes.txt	2009-05-26 04:14:03 +0000
+++ b/storage/ndb/src/kernel/blocks/ERROR_codes.txt	2009-09-01 10:50:11 +0000
@@ -6,7 +6,7 @@ Next DBTUP 4029
 Next DBLQH 5054
 Next DBDICT 6008
 Next DBDIH 7215
-Next DBTC 8074
+Next DBTC 8080
 Next CMVMI 9000
 Next BACKUP 10041
 Next DBUTIL 11002
@@ -348,6 +348,8 @@ ABORT OF TCKEYREQ
 8052 : Simulate failure of TransactionBufferMemory allocation for OI lookup
 
 8051 : Simulate failure of allocation for saveINDXKEYINFO
+8078 : Activate error insert 8079 on receiving API_FAILREQ
+8079 : Crash if TCKEYREQ received from failed api
 
 
 CMVMI

=== modified file 'storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp'
--- a/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp	2009-09-01 10:50:11 +0000
@@ -497,6 +497,7 @@ void Cmvmi::execCLOSE_COMREQ(Signal* sig
   CloseComReqConf * const closeCom = (CloseComReqConf *)&signal->theData[0];
 
   const BlockReference userRef = closeCom->xxxBlockRef;
+  Uint32 requestType = closeCom->requestType;
   Uint32 failNo = closeCom->failNo;
 //  Uint32 noOfNodes = closeCom->noOfNodes;
   
@@ -519,11 +520,20 @@ void Cmvmi::execCLOSE_COMREQ(Signal* sig
     }
   }
 
-  if (failNo != 0) 
+  if (requestType != CloseComReqConf::RT_NO_REPLY)
   {
+    ndbassert((requestType == CloseComReqConf::RT_API_FAILURE) ||
+              ((requestType == CloseComReqConf::RT_NODE_FAILURE) &&
+               (failNo != 0)));
     jam();
-    signal->theData[0] = userRef;
-    signal->theData[1] = failNo;
+    CloseComReqConf* closeComConf = (CloseComReqConf *)signal->getDataPtrSend();
+    closeComConf->xxxBlockRef = userRef;
+    closeComConf->requestType = requestType;
+    closeComConf->failNo = failNo;
+
+    /* Note assumption that noOfNodes and theNodes
+     * bitmap is not trampled above 
+     */
     sendSignal(QMGR_REF, GSN_CLOSE_COMCONF, signal, 19, JBA);
   }
 }

=== modified file 'storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp'
--- a/storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp	2009-09-01 12:27:40 +0000
@@ -1980,6 +1980,11 @@ private:
   /**************************************************************************/
 
   Uint32 c_gcp_ref;
+
+#ifdef ERROR_INSERT
+  // Used with ERROR_INSERT 8078 + 8079 to check API_FAILREQ handling
+  Uint32 c_lastFailedApi;
+#endif
 };
 
 #endif

=== modified file 'storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp'
--- a/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp	2009-08-04 10:52:04 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp	2009-09-01 12:27:40 +0000
@@ -936,6 +936,13 @@ void Dbtc::execAPI_FAILREQ(Signal* signa
     CLEAR_ERROR_INSERT_VALUE;
     return;
   }
+#ifdef ERROR_INSERT
+  if (ERROR_INSERTED(8078))
+  {
+    c_lastFailedApi = signal->theData[0];
+    SET_ERROR_INSERT_VALUE(8079);
+  }
+#endif
 
   capiFailRef = signal->theData[1];
   arrGuard(signal->theData[0], MAX_NODES);
@@ -1278,6 +1285,12 @@ void Dbtc::execTCSEIZEREQ(Signal* signal
     }
   } 
   
+  if (ERROR_INSERTED(8078) || ERROR_INSERTED(8079))
+  {
+    /* Clear testing of API_FAILREQ behaviour */
+    CLEAR_ERROR_INSERT_VALUE;
+  };
+
   seizeApiConnect(signal);
   if (terrorCode == ZOK) {
     jam();
@@ -2573,6 +2586,18 @@ void Dbtc::execTCKEYREQ(Signal* signal) 
     return;
   }//if
 
+#ifdef ERROR_INSERT
+  if (ERROR_INSERTED(8079))
+  {
+    /* Test that no signals received after API_FAILREQ */
+    if (sendersNodeId == c_lastFailedApi)
+    {
+      /* Signal from API node received *after* API_FAILREQ */
+      ndbrequire(false);
+    }
+  }
+#endif
+
   Treqinfo = tcKeyReq->requestInfo;
   //--------------------------------------------------------------------------
   // Optimised version of ptrAss(tabptr, tableRecord)
@@ -4118,7 +4143,8 @@ void Dbtc::sendtckeyconf(Signal* signal,
   if (unlikely(!ndb_check_micro_gcp(getNodeInfo(localHostptr.i).m_version)))
   {
     jam();
-    ndbassert(Tpack6 == 0 || getNodeInfo(localHostptr.i).m_connected == false);
+    ndbassert(Tpack6 == 0 || 
+              getNodeInfo(localHostptr.i).m_version == 0); // Disconnected
   }
 }//Dbtc::sendtckeyconf()
 

=== modified file 'storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp'
--- a/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp	2009-09-04 10:15:28 +0000
@@ -571,9 +571,9 @@ typedef Ptr<Fragoperrec> FragoperrecPtr;
     Local_key m_key;
     Uint32 m_frag_ptr_i;
     Uint32 m_extent_info_ptr;
-    Uint16 m_estimated_free_space; // in bytes/records
-    Uint16 m_list_index;           // in Disk_alloc_info.m_page_requests
-    Uint16 m_ref_count;            // Waiters for page
+    Uint16 m_original_estimated_free_space; // in bytes/records
+    Uint16 m_list_index;                  // in Disk_alloc_info.m_page_requests
+    Uint16 m_ref_count;                   // Waiters for page
     Uint16 m_uncommitted_used_space;
     Uint32 nextList;
     Uint32 prevList;
@@ -3140,9 +3140,19 @@ private:
   void drop_table_logsync_callback(Signal*, Uint32, Uint32);
 
   void disk_page_set_dirty(Ptr<Page>);
-  void restart_setup_page(Disk_alloc_info&, Ptr<Page>);
-  void update_extent_pos(Disk_alloc_info&, Ptr<Extent_info>);
-  
+  void restart_setup_page(Disk_alloc_info&, Ptr<Page>, Int32 estimate);
+  void update_extent_pos(Disk_alloc_info&, Ptr<Extent_info>, Int32 delta);
+
+  void disk_page_move_page_request(Disk_alloc_info& alloc,
+                                   Ptr<Extent_info>,
+                                   Ptr<Page_request> req,
+                                   Uint32 old_idx, Uint32 new_idx);
+
+  void disk_page_move_dirty_page(Disk_alloc_info& alloc,
+                                 Ptr<Extent_info> extentPtr,
+                                 Ptr<Page> pagePtr,
+                                 Uint32 old_idx, Uint32 new_idx);
+
   /**
    * Disk restart code
    */

=== modified file 'storage/ndb/src/kernel/blocks/dbtup/DbtupDiskAlloc.cpp'
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupDiskAlloc.cpp	2009-05-28 12:43:04 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupDiskAlloc.cpp	2009-09-04 15:19:41 +0000
@@ -42,7 +42,7 @@ operator<<(NdbOut& out, const Ptr<Dbtup:
 {
   out << "[ Page_request: ptr.i: " << ptr.i
       << " " << ptr.p->m_key
-      << " m_estimated_free_space: " << ptr.p->m_estimated_free_space
+      << " m_original_estimated_free_space: " << ptr.p->m_original_estimated_free_space
       << " m_list_index: " << ptr.p->m_list_index
       << " m_frag_ptr_i: " << ptr.p->m_frag_ptr_i
       << " m_extent_info_ptr: " << ptr.p->m_extent_info_ptr
@@ -250,16 +250,41 @@ Dbtup::Disk_alloc_info::calc_extent_pos(
 
 void
 Dbtup::update_extent_pos(Disk_alloc_info& alloc, 
-			 Ptr<Extent_info> extentPtr)
+                         Ptr<Extent_info> extentPtr,
+                         Int32 delta)
 {
+  if (delta < 0)
+  {
+    jam();
+    Uint32 sub = Uint32(- delta);
+    ddassert(extentPtr.p->m_free_space >= sub);
+    extentPtr.p->m_free_space -= sub;
+  }
+  else
+  {
+    jam();
+    extentPtr.p->m_free_space += delta;
+    ndbassert(Uint32(delta) <= alloc.calc_page_free_space(0));
+  }
+
 #ifdef VM_TRACE
-  Uint32 min_free = 0;
+  Uint32 cnt = 0;
+  Uint32 sum = 0;
   for(Uint32 i = 0; i<MAX_FREE_LIST; i++)
   {
-    Uint32 sum = alloc.calc_page_free_space(i);
-    min_free += sum * extentPtr.p->m_free_page_count[i];
+    cnt += extentPtr.p->m_free_page_count[i];
+    sum += extentPtr.p->m_free_page_count[i] * alloc.calc_page_free_space(i);
+  }
+  if (extentPtr.p->m_free_page_count[0] == cnt)
+  {
+    ddassert(extentPtr.p->m_free_space == cnt*alloc.m_page_free_bits_map[0]);
   }
-  ddassert(extentPtr.p->m_free_space >= min_free);
+  else
+  {
+    ddassert(extentPtr.p->m_free_space < cnt*alloc.m_page_free_bits_map[0]);
+  }
+  ddassert(extentPtr.p->m_free_space >= sum);
+  ddassert(extentPtr.p->m_free_space <= cnt*alloc.m_page_free_bits_map[0]);
 #endif
   
   Uint32 old = extentPtr.p->m_free_matrix_pos;
@@ -283,7 +308,8 @@ Dbtup::update_extent_pos(Disk_alloc_info
 }
 
 void
-Dbtup::restart_setup_page(Disk_alloc_info& alloc, PagePtr pagePtr)
+Dbtup::restart_setup_page(Disk_alloc_info& alloc, PagePtr pagePtr,
+                          Int32 estimate)
 {
   jam();
   /**
@@ -299,16 +325,24 @@ Dbtup::restart_setup_page(Disk_alloc_inf
   ndbrequire(c_extent_hash.find(extentPtr, key));
   pagePtr.p->m_extent_info_ptr = extentPtr.i;
 
-  Uint32 idx = pagePtr.p->list_index & ~0x8000;
-  Uint32 estimated = alloc.calc_page_free_space(idx);
   Uint32 real_free = pagePtr.p->free_space;
-
-  ddassert(real_free >= estimated);
-  if (real_free != estimated)
+  const bool prealloc = estimate >= 0;
+  Uint32 estimated;
+  if (prealloc)
+  {
+    jam();
+    /**
+     * If this is during prealloc, use estimate from there
+     */
+    estimated = (Uint32)estimate;
+  }
+  else
   {
     jam();
-    extentPtr.p->m_free_space += (real_free - estimated);
-    update_extent_pos(alloc, extentPtr);
+    /**
+     * else use the estimate based on the actual free space
+     */
+    estimated =alloc.calc_page_free_space(alloc.calc_page_free_bits(real_free));
   }
 
 #ifdef VM_TRACE
@@ -324,10 +358,30 @@ Dbtup::restart_setup_page(Disk_alloc_inf
     (void) tsman.get_page_free_bits(&page, &uncommitted, &committed);
     jamEntry();
     
-    idx = alloc.calc_page_free_bits(real_free);
-    ddassert(idx == committed);
+    ddassert(alloc.calc_page_free_bits(real_free) == committed);
+    if (prealloc)
+    {
+      /**
+       * tsman.alloc_page sets the uncommitted-bits to MAX_FREE_LIST -1
+       *   to avoid page being preallocated several times
+       */
+      ddassert(uncommitted == MAX_FREE_LIST - 1);
+    }
+    else
+    {
+      ddassert(committed == uncommitted);
+    }
   }
 #endif
+
+  ddassert(real_free >= estimated);
+
+  if (real_free != estimated)
+  {
+    jam();
+    Uint32 delta = (real_free-estimated);
+    update_extent_pos(alloc, extentPtr, delta);
+  }
 }
 
 /**
@@ -524,6 +578,7 @@ Dbtup::disk_page_prealloc(Signal* signal
 	  
 	  Logfile_client lgman(this, c_lgman, logfile_group_id);
 	  int res= lgman.get_log_buffer(signal, sz, &cb);
+          jamEntry();
 	  switch(res){
 	  case 0:
 	    break;
@@ -575,9 +630,9 @@ Dbtup::disk_page_prealloc(Signal* signal
   Uint32 size= alloc.calc_page_free_space((Uint32)pageBits);
   
   ddassert(size >= sz);
-  Uint32 new_size = size - sz;   // Subtract alloc rec
-  req.p->m_estimated_free_space= new_size; // Store on page request
+  req.p->m_original_estimated_free_space = size;
 
+  Uint32 new_size = size - sz;   // Subtract alloc rec
   Uint32 newPageBits= alloc.calc_page_free_bits(new_size);
   if (newPageBits != (Uint32)pageBits)
   {
@@ -586,9 +641,8 @@ Dbtup::disk_page_prealloc(Signal* signal
     ext.p->m_free_page_count[pageBits]--;
     ext.p->m_free_page_count[newPageBits]++;
   }
-  ddassert(ext.p->m_free_space >= sz);
-  ext.p->m_free_space -= sz;
-  
+  update_extent_pos(alloc, ext, -Int32(sz));
+
   // And put page request in correct free list
   idx= alloc.calc_page_free_bits(new_size);
   {
@@ -651,26 +705,15 @@ Dbtup::disk_page_prealloc_dirty_page(Dis
   c_extent_pool.getPtr(extentPtr, ext);
 
   Uint32 new_idx= alloc.calc_page_free_bits(free - used);
-  ArrayPool<Page> *pool= (ArrayPool<Page>*)&m_global_page_pool;
 
   if (old_idx != new_idx)
   {
     jam();
-    LocalDLList<Page> old_list(*pool, alloc.m_dirty_pages[old_idx]);
-    LocalDLList<Page> new_list(*pool, alloc.m_dirty_pages[new_idx]);
-    old_list.remove(pagePtr);
-    new_list.add(pagePtr);
-
-    ddassert(extentPtr.p->m_free_page_count[old_idx]);
-    extentPtr.p->m_free_page_count[old_idx]--;
-    extentPtr.p->m_free_page_count[new_idx]++;
-    pagePtr.p->list_index= new_idx;  
+    disk_page_move_dirty_page(alloc, extentPtr, pagePtr, old_idx, new_idx);
   }
 
   pagePtr.p->uncommitted_used_space = used;
-  ddassert(extentPtr.p->m_free_space >= sz);
-  extentPtr.p->m_free_space -= sz;
-  update_extent_pos(alloc, extentPtr);
+  update_extent_pos(alloc, extentPtr, -Int32(sz));
 }
 
 
@@ -682,39 +725,26 @@ Dbtup::disk_page_prealloc_transit_page(D
   jam();
   ddassert(req.p->m_list_index == old_idx);
 
-  Uint32 free= req.p->m_estimated_free_space;
+  Uint32 free= req.p->m_original_estimated_free_space;
   Uint32 used= req.p->m_uncommitted_used_space + sz;
   Uint32 ext= req.p->m_extent_info_ptr;
   
   Ptr<Extent_info> extentPtr;
   c_extent_pool.getPtr(extentPtr, ext);
 
-  ddassert(free >= sz);
-  Uint32 new_idx= alloc.calc_page_free_bits(free - sz);
+  ddassert(free >= used);
+  Uint32 new_idx= alloc.calc_page_free_bits(free - used);
   
   if (old_idx != new_idx)
   {
     jam();
-    Page_request_list::Head *lists = alloc.m_page_requests;
-    Local_page_request_list old_list(c_page_request_pool, lists[old_idx]);
-    Local_page_request_list new_list(c_page_request_pool, lists[new_idx]);
-    old_list.remove(req);
-    new_list.add(req);
-
-    ddassert(extentPtr.p->m_free_page_count[old_idx]);
-    extentPtr.p->m_free_page_count[old_idx]--;
-    extentPtr.p->m_free_page_count[new_idx]++;
-    req.p->m_list_index= new_idx;  
+    disk_page_move_page_request(alloc, extentPtr, req, old_idx, new_idx);
   }
 
   req.p->m_uncommitted_used_space = used;
-  req.p->m_estimated_free_space = free - sz;
-  ddassert(extentPtr.p->m_free_space >= sz);
-  extentPtr.p->m_free_space -= sz;
-  update_extent_pos(alloc, extentPtr);
+  update_extent_pos(alloc, extentPtr, -Int32(sz));
 }
 
-
 void
 Dbtup::disk_page_prealloc_callback(Signal* signal, 
 				   Uint32 page_request, Uint32 page_id)
@@ -736,12 +766,87 @@ Dbtup::disk_page_prealloc_callback(Signa
   pagePtr.i = gpage.i;
   pagePtr.p = reinterpret_cast<Page*>(gpage.p);
 
+  Disk_alloc_info& alloc= fragPtr.p->m_disk_alloc_info;
   if (unlikely(pagePtr.p->m_restart_seq != globalData.m_restart_seq))
   {
-    restart_setup_page(fragPtr.p->m_disk_alloc_info, pagePtr);
+    jam();
+    restart_setup_page(alloc, pagePtr, req.p->m_original_estimated_free_space);
+  }
+
+  Ptr<Extent_info> extentPtr;
+  c_extent_pool.getPtr(extentPtr, req.p->m_extent_info_ptr);
+
+  pagePtr.p->uncommitted_used_space += req.p->m_uncommitted_used_space;
+  ddassert(pagePtr.p->free_space >= pagePtr.p->uncommitted_used_space);
+
+  Uint32 free = pagePtr.p->free_space - pagePtr.p->uncommitted_used_space;
+  Uint32 idx = req.p->m_list_index;
+  Uint32 real_idx = alloc.calc_page_free_bits(free);
+
+  if (idx != real_idx)
+  {
+    jam();
+    ddassert(extentPtr.p->m_free_page_count[idx]);
+    extentPtr.p->m_free_page_count[idx]--;
+    extentPtr.p->m_free_page_count[real_idx]++;
+    update_extent_pos(alloc, extentPtr, 0);
   }
 
-  disk_page_prealloc_callback_common(signal, req, fragPtr, pagePtr);
+  {
+    /**
+     * add to dirty list
+     */
+    pagePtr.p->list_index = real_idx;
+    ArrayPool<Page> *cheat_pool= (ArrayPool<Page>*)&m_global_page_pool;
+    LocalDLList<Page> list(* cheat_pool, alloc.m_dirty_pages[real_idx]);
+    list.add(pagePtr);
+  }
+
+  {
+    /**
+     * release page request
+     */
+    Local_page_request_list list(c_page_request_pool,
+				 alloc.m_page_requests[idx]);
+    list.release(req);
+  }
+}
+
+void
+Dbtup::disk_page_move_dirty_page(Disk_alloc_info& alloc,
+                                 Ptr<Extent_info> extentPtr,
+                                 Ptr<Page> pagePtr,
+                                 Uint32 old_idx,
+                                 Uint32 new_idx)
+{
+  ddassert(extentPtr.p->m_free_page_count[old_idx]);
+  extentPtr.p->m_free_page_count[old_idx]--;
+  extentPtr.p->m_free_page_count[new_idx]++;
+
+  ArrayPool<Page> *pool= (ArrayPool<Page>*)&m_global_page_pool;
+  LocalDLList<Page> new_list(*pool, alloc.m_dirty_pages[new_idx]);
+  LocalDLList<Page> old_list(*pool, alloc.m_dirty_pages[old_idx]);
+  old_list.remove(pagePtr);
+  new_list.add(pagePtr);
+  pagePtr.p->list_index = new_idx;
+}
+
+void
+Dbtup::disk_page_move_page_request(Disk_alloc_info& alloc,
+                                   Ptr<Extent_info> extentPtr,
+                                   Ptr<Page_request> req,
+                                   Uint32 old_idx, Uint32 new_idx)
+{
+  Page_request_list::Head *lists = alloc.m_page_requests;
+  Local_page_request_list old_list(c_page_request_pool, lists[old_idx]);
+  Local_page_request_list new_list(c_page_request_pool, lists[new_idx]);
+  old_list.remove(req);
+  new_list.add(req);
+
+  ddassert(extentPtr.p->m_free_page_count[old_idx]);
+  extentPtr.p->m_free_page_count[old_idx]--;
+  extentPtr.p->m_free_page_count[new_idx]++;
+  req.p->m_list_index= new_idx;
 }
 
 void
@@ -778,17 +883,6 @@ Dbtup::disk_page_prealloc_initial_callba
   Ptr<Extent_info> extentPtr;
   c_extent_pool.getPtr(extentPtr, req.p->m_extent_info_ptr);
 
-  pagePtr.p->m_page_no= req.p->m_key.m_page_no;
-  pagePtr.p->m_file_no= req.p->m_key.m_file_no;
-  pagePtr.p->m_table_id= fragPtr.p->fragTableId;
-  pagePtr.p->m_fragment_id = fragPtr.p->fragmentId;
-  pagePtr.p->m_extent_no = extentPtr.p->m_key.m_page_idx; // logical extent no
-  pagePtr.p->m_extent_info_ptr= req.p->m_extent_info_ptr;
-  pagePtr.p->m_restart_seq = globalData.m_restart_seq;
-  pagePtr.p->list_index = 0x8000;
-  pagePtr.p->uncommitted_used_space = 0;
-  pagePtr.p->nextList = pagePtr.p->prevList = RNIL;
-  
   if (tabPtr.p->m_attributes[DD].m_no_of_varsize == 0)
   {
     convertThPage((Fix_page*)pagePtr.p, tabPtr.p, DD);
@@ -797,70 +891,42 @@ Dbtup::disk_page_prealloc_initial_callba
   {
     abort();
   }
-  disk_page_prealloc_callback_common(signal, req, fragPtr, pagePtr);
-}
 
-void
-Dbtup::disk_page_prealloc_callback_common(Signal* signal, 
-					  Ptr<Page_request> req, 
-					  Ptr<Fragrecord> fragPtr, 
-					  PagePtr pagePtr)
-{
-  /**
-   * 1) remove page request from Disk_alloc_info.m_page_requests
-   * 2) Add page to Disk_alloc_info.m_dirty_pages
-   * 3) register callback in pgman (unmap callback)
-   * 4) inform pgman about current users
-   */
-  Disk_alloc_info& alloc= fragPtr.p->m_disk_alloc_info;
-  ddassert((pagePtr.p->list_index & 0x8000) == 0x8000);
-  ddassert(pagePtr.p->m_extent_info_ptr == req.p->m_extent_info_ptr);
-  ddassert(pagePtr.p->m_page_no == req.p->m_key.m_page_no);
-  ddassert(pagePtr.p->m_file_no == req.p->m_key.m_file_no);
-  
-  Uint32 old_idx = req.p->m_list_index;
-  Uint32 free= req.p->m_estimated_free_space;
-  Uint32 ext = req.p->m_extent_info_ptr;
-  Uint32 used= req.p->m_uncommitted_used_space;
-  Uint32 real_free = pagePtr.p->free_space;
-  Uint32 real_used = used + pagePtr.p->uncommitted_used_space;
- 
-  ddassert(real_free >= free);
-  ddassert(real_free >= real_used);
-  ddassert(alloc.calc_page_free_bits(free) == old_idx);
-  Uint32 new_idx= alloc.calc_page_free_bits(real_free - real_used);
+  pagePtr.p->m_page_no= req.p->m_key.m_page_no;
+  pagePtr.p->m_file_no= req.p->m_key.m_file_no;
+  pagePtr.p->m_table_id= fragPtr.p->fragTableId;
+  pagePtr.p->m_fragment_id = fragPtr.p->fragmentId;
+  pagePtr.p->m_extent_no = extentPtr.p->m_key.m_page_idx; // logical extent no
+  pagePtr.p->m_extent_info_ptr= req.p->m_extent_info_ptr;
+  pagePtr.p->m_restart_seq = globalData.m_restart_seq;
+  pagePtr.p->nextList = pagePtr.p->prevList = RNIL;
+  pagePtr.p->list_index = req.p->m_list_index;
+  pagePtr.p->uncommitted_used_space = req.p->m_uncommitted_used_space;
 
-  /**
-   * Add to dirty pages
-   */
-  ArrayPool<Page> *cheat_pool= (ArrayPool<Page>*)&m_global_page_pool;
-  LocalDLList<Page> list(* cheat_pool, alloc.m_dirty_pages[new_idx]);
-  list.add(pagePtr);
-  pagePtr.p->uncommitted_used_space = real_used;
-  pagePtr.p->list_index = new_idx;
+  Disk_alloc_info& alloc= fragPtr.p->m_disk_alloc_info;
+  Uint32 idx = req.p->m_list_index;
 
-  if (old_idx != new_idx || free != real_free)
   {
-    jam();
-    Ptr<Extent_info> extentPtr;
-    c_extent_pool.getPtr(extentPtr, ext);
+    Uint32 free = pagePtr.p->free_space - pagePtr.p->uncommitted_used_space;
+    ddassert(idx == alloc.calc_page_free_bits(free));
+    ddassert(pagePtr.p->free_space == req.p->m_original_estimated_free_space);
+  }
 
-    extentPtr.p->m_free_space += (real_free - free);
-    
-    if (old_idx != new_idx)
-    {
-      jam();
-      ddassert(extentPtr.p->m_free_page_count[old_idx]);
-      extentPtr.p->m_free_page_count[old_idx]--;
-      extentPtr.p->m_free_page_count[new_idx]++;
-    }
-    
-    update_extent_pos(alloc, extentPtr);
+  {
+    /**
+     * add to dirty list
+     */
+    ArrayPool<Page> *cheat_pool= (ArrayPool<Page>*)&m_global_page_pool;
+    LocalDLList<Page> list(* cheat_pool, alloc.m_dirty_pages[idx]);
+    list.add(pagePtr);
   }
-  
+
   {
+    /**
+     * release page request
+     */
     Local_page_request_list list(c_page_request_pool, 
-				 alloc.m_page_requests[old_idx]);
+				 alloc.m_page_requests[idx]);
     list.release(req);
   }
 }
@@ -870,7 +936,8 @@ Dbtup::disk_page_set_dirty(PagePtr pageP
 {
   jam();
   Uint32 idx = pagePtr.p->list_index;
-  if ((idx & 0x8000) == 0)
+  if ((pagePtr.p->m_restart_seq == globalData.m_restart_seq) &&
+      ((idx & 0x8000) == 0))
   {
     jam();
     /**
@@ -901,12 +968,15 @@ Dbtup::disk_page_set_dirty(PagePtr pageP
   Uint32 used = pagePtr.p->uncommitted_used_space;
   if (unlikely(pagePtr.p->m_restart_seq != globalData.m_restart_seq))
   {
-    restart_setup_page(alloc, pagePtr);
+    jam();
+    restart_setup_page(alloc, pagePtr, -1);
+    ndbassert(free == pagePtr.p->free_space);
     idx = alloc.calc_page_free_bits(free);
     used = 0;
   }
   else
   {
+    jam();
     idx &= ~0x8000;
     ddassert(idx == alloc.calc_page_free_bits(free - used));
   }
@@ -1131,20 +1201,10 @@ Dbtup::disk_page_free(Signal *signal, 
   if (old_idx != new_idx)
   {
     jam();
-    ddassert(extentPtr.p->m_free_page_count[old_idx]);
-    extentPtr.p->m_free_page_count[old_idx]--;
-    extentPtr.p->m_free_page_count[new_idx]++;
-
-    ArrayPool<Page> *pool= (ArrayPool<Page>*)&m_global_page_pool;
-    LocalDLList<Page> new_list(*pool, alloc.m_dirty_pages[new_idx]);
-    LocalDLList<Page> old_list(*pool, alloc.m_dirty_pages[old_idx]);
-    old_list.remove(pagePtr);
-    new_list.add(pagePtr);
-    pagePtr.p->list_index = new_idx;
+    disk_page_move_dirty_page(alloc, extentPtr, pagePtr, old_idx, new_idx);
   }
   
-  extentPtr.p->m_free_space += sz;
-  update_extent_pos(alloc, extentPtr);
+  update_extent_pos(alloc, extentPtr, sz);
 #if NOT_YET_FREE_EXTENT
   if (check_free(extentPtr.p) == 0)
   {
@@ -1158,6 +1218,7 @@ Dbtup::disk_page_abort_prealloc(Signal *
 				Local_key* key, Uint32 sz)
 {
   jam();
+
   Page_cache_client::Request req;
   req.m_callback.m_callbackData= sz;
   req.m_callback.m_callbackFunction = 
@@ -1221,47 +1282,29 @@ Dbtup::disk_page_abort_prealloc_callback
   disk_page_set_dirty(pagePtr);
 
   Disk_alloc_info& alloc= fragPtrP->m_disk_alloc_info;
-  Uint32 page_idx = pagePtr.p->list_index;
+
+  Ptr<Extent_info> extentPtr;
+  c_extent_pool.getPtr(extentPtr, pagePtr.p->m_extent_info_ptr);
+
+  Uint32 idx = pagePtr.p->list_index & 0x7FFF;
   Uint32 used = pagePtr.p->uncommitted_used_space;
   Uint32 free = pagePtr.p->free_space;
-  Uint32 ext = pagePtr.p->m_extent_info_ptr;
 
-  Uint32 old_idx = page_idx & 0x7FFF;
   ddassert(free >= used);
   ddassert(used >= sz);
-  ddassert(alloc.calc_page_free_bits(free - used) == old_idx);
+  ddassert(alloc.calc_page_free_bits(free - used) == idx);
+
+  pagePtr.p->uncommitted_used_space = used - sz;
+
   Uint32 new_idx = alloc.calc_page_free_bits(free - used + sz);
 
-  Ptr<Extent_info> extentPtr;
-  c_extent_pool.getPtr(extentPtr, ext);
-  if (old_idx != new_idx)
+  if (idx != new_idx)
   {
     jam();
-    ddassert(extentPtr.p->m_free_page_count[old_idx]);
-    extentPtr.p->m_free_page_count[old_idx]--;
-    extentPtr.p->m_free_page_count[new_idx]++;
-
-    if (old_idx == page_idx)
-    {
-      jam();
-      ArrayPool<Page> *pool= (ArrayPool<Page>*)&m_global_page_pool;
-      LocalDLList<Page> old_list(*pool, alloc.m_dirty_pages[old_idx]);
-      LocalDLList<Page> new_list(*pool, alloc.m_dirty_pages[new_idx]);
-      old_list.remove(pagePtr);
-      new_list.add(pagePtr);
-      pagePtr.p->list_index = new_idx;
-    }
-    else
-    {
-      jam();
-      pagePtr.p->list_index = new_idx | 0x8000;
-    }
+    disk_page_move_dirty_page(alloc, extentPtr, pagePtr, idx, new_idx);
   }
   
-  pagePtr.p->uncommitted_used_space = used - sz;
-
-  extentPtr.p->m_free_space += sz;
-  update_extent_pos(alloc, extentPtr);
+  update_extent_pos(alloc, extentPtr, sz);
 #if NOT_YET_FREE_EXTENT
   if (check_free(extentPtr.p) == 0)
   {
@@ -1322,6 +1365,7 @@ Dbtup::disk_page_undo_alloc(Page* page, 
   Logfile_client::Change c[1] = {{ &alloc, sizeof(alloc) >> 2 } };
   
   Uint64 lsn= lgman.add_entry(c, 1);
+  jamEntry();
   m_pgman.update_lsn(* key, lsn);
   jamEntry();
 
@@ -1353,6 +1397,7 @@ Dbtup::disk_page_undo_update(Page* page,
   ndbassert(4*(3 + sz + 1) == (sizeof(update) + 4*sz - 4));
     
   Uint64 lsn= lgman.add_entry(c, 3);
+  jamEntry();
   m_pgman.update_lsn(* key, lsn);
   jamEntry();
 
@@ -1384,6 +1429,7 @@ Dbtup::disk_page_undo_free(Page* page, c
   ndbassert(4*(3 + sz + 1) == (sizeof(free) + 4*sz - 4));
   
   Uint64 lsn= lgman.add_entry(c, 3);
+  jamEntry();
   m_pgman.update_lsn(* key, lsn);
   jamEntry();
 
@@ -1882,7 +1928,7 @@ Dbtup::disk_restart_alloc_extent(Uint32 
 
 void
 Dbtup::disk_restart_page_bits(Uint32 tableId, Uint32 fragId,
-			      const Local_key*, Uint32 bits)
+			      const Local_key* key, Uint32 bits)
 {
   jam();
   TablerecPtr tabPtr;
@@ -1897,7 +1943,7 @@ Dbtup::disk_restart_page_bits(Uint32 tab
   
   Uint32 size= alloc.calc_page_free_space(bits);  
   
-  ext.p->m_free_space += size;
   ext.p->m_free_page_count[bits]++;
+  update_extent_pos(alloc, ext, size); // actually only to update free_space
   ndbassert(ext.p->m_free_matrix_pos == RNIL);
 }

=== modified file 'storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp'
--- a/storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp	2009-09-04 10:15:28 +0000
@@ -350,6 +350,7 @@ void Dbtup::execTUP_ADD_ATTRREQ(Signal* 
 	Tablespace_client tsman(0, c_tsman, 0, 0, 
 				regFragPtr.p->m_tablespace_id);
 	ndbrequire(tsman.get_tablespace_info(&rep) == 0);
+        jamEntry();
 	regFragPtr.p->m_logfile_group_id= rep.tablespace.logfile_group_id;
       }
       else
@@ -549,6 +550,7 @@ void Dbtup::execTUP_ADD_ATTRREQ(Signal* 
     Tablespace_client tsman(0, c_tsman, 0, 0, 
 			    regFragPtr.p->m_tablespace_id);
     ndbrequire(tsman.get_tablespace_info(&rep) == 0);
+    jamEntry();
     regFragPtr.p->m_logfile_group_id= rep.tablespace.logfile_group_id;
   }
   else
@@ -578,11 +580,14 @@ void Dbtup::execTUP_ADD_ATTRREQ(Signal* 
       if((terrorCode = 
           c_lgman->alloc_log_space(regFragPtr.p->m_logfile_group_id, sz)))
       {
+        jamEntry();
         addattrrefuseLab(signal, regFragPtr, fragOperPtr, regTabPtr.p, fragId);
         return;
       }
       
+      jamEntry();
       int res= lgman.get_log_buffer(signal, sz, &cb);
+      jamEntry();
       switch(res){
       case 0:
         jam();
@@ -1201,6 +1206,7 @@ Dbtup::undo_createtable_callback(Signal*
   Logfile_client::Change c[1] = {{ &create, sizeof(create) >> 2 } };
   
   Uint64 lsn= lgman.add_entry(c, 1);
+  jamEntry();
 
   Logfile_client::Request req;
   req.m_callback.m_callbackData= fragOperPtr.i;
@@ -1208,6 +1214,7 @@ Dbtup::undo_createtable_callback(Signal*
     safe_cast(&Dbtup::undo_createtable_logsync_callback);
   
   int ret = lgman.sync_lsn(signal, lsn, &req, 0);
+  jamEntry();
   switch(ret){
   case 0:
     return;
@@ -1586,6 +1593,7 @@ void Dbtup::releaseFragment(Signal* sign
       safe_cast(&Dbtup::drop_table_log_buffer_callback);
     Uint32 sz= sizeof(Disk_undo::Drop) >> 2;
     int r0 = c_lgman->alloc_log_space(logfile_group_id, sz);
+    jamEntry();
     if (r0)
     {
       jam();
@@ -1596,6 +1604,7 @@ void Dbtup::releaseFragment(Signal* sign
 
     Logfile_client lgman(this, c_lgman, logfile_group_id);
     int res= lgman.get_log_buffer(signal, sz, &cb);
+    jamEntry();
     switch(res){
     case 0:
       jam();
@@ -1604,6 +1613,7 @@ void Dbtup::releaseFragment(Signal* sign
       warningEvent("Failed to get log buffer for drop table: %u",
 		   tabPtr.i);
       c_lgman->free_log_space(logfile_group_id, sz);
+      jamEntry();
       goto done;
       break;
     default:
@@ -1630,7 +1640,6 @@ Dbtup::drop_fragment_unmap_pages(Signal 
     if (!alloc_info.m_unmap_pages.isEmpty())
     {
       jam();
-      ndbout_c("waiting for unmape pages");
       signal->theData[0] = ZUNMAP_PAGES;
       signal->theData[1] = tabPtr.i;
       signal->theData[2] = fragPtr.i;
@@ -1675,6 +1684,7 @@ Dbtup::drop_fragment_unmap_pages(Signal 
     
     int flags= Page_cache_client::COMMIT_REQ;
     int res= m_pgman.get_page(signal, req, flags);
+    jamEntry();
     switch(res)
     {
     case 0:
@@ -1703,6 +1713,7 @@ Dbtup::drop_fragment_unmap_page_callback
   Uint32 fragId = ((Page*)page.p)->m_fragment_id;
   Uint32 tableId = ((Page*)page.p)->m_table_id;
   m_pgman.drop_page(key, page_id);
+  jamEntry();
 
   TablerecPtr tabPtr;
   tabPtr.i= tableId;
@@ -1739,10 +1750,12 @@ Dbtup::drop_fragment_free_extent(Signal 
 #if NOT_YET_UNDO_FREE_EXTENT
 	Uint32 sz= sizeof(Disk_undo::FreeExtent) >> 2;
 	(void) c_lgman->alloc_log_space(fragPtr.p->m_logfile_group_id, sz);
+        jamEntry();
 	
 	Logfile_client lgman(this, c_lgman, fragPtr.p->m_logfile_group_id);
 	
 	int res= lgman.get_log_buffer(signal, sz, &cb);
+        jamEntry();
 	switch(res){
 	case 0:
 	  jam();
@@ -1794,6 +1807,7 @@ Dbtup::drop_table_log_buffer_callback(Si
   
   Logfile_client::Change c[1] = {{ &drop, sizeof(drop) >> 2 } };
   Uint64 lsn = lgman.add_entry(c, 1);
+  jamEntry();
 
   Logfile_client::Request req;
   req.m_callback.m_callbackData= tablePtrI;
@@ -1801,6 +1815,7 @@ Dbtup::drop_table_log_buffer_callback(Si
     safe_cast(&Dbtup::drop_table_logsync_callback);
   
   int ret = lgman.sync_lsn(signal, lsn, &req, 0);
+  jamEntry();
   switch(ret){
   case 0:
     return;
@@ -1871,6 +1886,7 @@ Dbtup::drop_fragment_free_extent_log_buf
       
       Logfile_client::Change c[1] = {{ &free, sizeof(free) >> 2 } };
       Uint64 lsn = lgman.add_entry(c, 1);
+      jamEntry();
 #else
       Uint64 lsn = 0;
 #endif
@@ -1880,6 +1896,7 @@ Dbtup::drop_fragment_free_extent_log_buf
 			      fragPtr.p->m_tablespace_id);
       
       tsman.free_extent(&ext_ptr.p->m_key, lsn);
+      jamEntry();
       c_extent_hash.remove(ext_ptr);
       list.release(ext_ptr);
       

=== modified file 'storage/ndb/src/kernel/blocks/qmgr/Qmgr.hpp'
--- a/storage/ndb/src/kernel/blocks/qmgr/Qmgr.hpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/src/kernel/blocks/qmgr/Qmgr.hpp	2009-09-01 10:50:11 +0000
@@ -92,10 +92,12 @@ public:
   
   enum FailState {
     NORMAL = 0,
-    WAITING_FOR_FAILCONF1 = 1,
-    WAITING_FOR_FAILCONF2 = 2,
-    WAITING_FOR_FAILCONF3 = 3,
-    WAITING_FOR_NDB_FAILCONF = 3
+    WAITING_FOR_CLOSECOMCONF_ACTIVE = 1,     /* Node had phase ZAPI_ACTIVE */
+    WAITING_FOR_CLOSECOMCONF_NOTACTIVE = 2,  /* Node had phase != ZAPI_ACTIVE */
+    WAITING_FOR_FAILCONF1 = 3,
+    WAITING_FOR_FAILCONF2 = 4,
+    WAITING_FOR_FAILCONF3 = 5,
+    WAITING_FOR_NDB_FAILCONF = 6
   };
 
   enum Phase {
@@ -391,7 +393,8 @@ private:
 			  Uint32 failNo,
 			  Uint32 noOfNodes,
 			  const NodeId theNodes[]);
-    
+
+  void handleApiCloseComConf(Signal* signal);
 
   
   /* Wait this time until we try to join the       */

=== modified file 'storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp'
--- a/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp	2009-09-01 10:50:11 +0000
@@ -2618,14 +2618,14 @@ void Qmgr::sendApiFailReq(Signal* signal
 
   ptrCheckGuard(failedNodePtr, MAX_NODES, nodeRec);
   
-  ndbrequire(failedNodePtr.p->failState == NORMAL);
-  
   failedNodePtr.p->failState = WAITING_FOR_FAILCONF1;
-  NodeReceiverGroup rg(QMGR, c_clusterNodes);
-  sendSignal(rg, GSN_API_FAILREQ, signal, 2, JBA);
-  sendSignal(DBTC_REF, GSN_API_FAILREQ, signal, 2, JBA);
-  sendSignal(DBDICT_REF, GSN_API_FAILREQ, signal, 2, JBA);
-  sendSignal(SUMA_REF, GSN_API_FAILREQ, signal, 2, JBA);
+
+  /* JBB used to ensure delivery *after* any pending
+   * signals
+   */
+  sendSignal(DBTC_REF, GSN_API_FAILREQ, signal, 2, JBB);
+  sendSignal(DBDICT_REF, GSN_API_FAILREQ, signal, 2, JBB);
+  sendSignal(SUMA_REF, GSN_API_FAILREQ, signal, 2, JBB);
 }//Qmgr::sendApiFailReq()
 
 void Qmgr::execAPI_FAILREQ(Signal* signal)
@@ -2888,6 +2888,7 @@ void Qmgr::node_failed(Signal* signal, U
       (CloseComReqConf *)&signal->theData[0];
 
     closeCom->xxxBlockRef = reference();
+    closeCom->requestType = CloseComReqConf::RT_NO_REPLY;
     closeCom->failNo      = 0;
     closeCom->noOfNodes   = 1;
     NodeBitmask::clear(closeCom->theNodes);
@@ -2931,25 +2932,24 @@ Qmgr::api_failed(Signal* signal, Uint32 
     return;
   }
 
-  if (failedNodePtr.p->phase == ZAPI_ACTIVE)
-  {
-    jam();
-    sendApiFailReq(signal, nodeId);
-    arbitRec.code = ArbitCode::ApiFail;
-    handleArbitApiFail(signal, nodeId);
-  }
-  else
-  {
-    /**
-     * Always inform SUMA
-     */
-    jam();
-    signal->theData[0] = nodeId;
-    signal->theData[1] = QMGR_REF;
-    sendSignal(SUMA_REF, GSN_API_FAILREQ, signal, 2, JBA);
-    failedNodePtr.p->failState = WAITING_FOR_FAILCONF3;
-  }
+  ndbrequire(failedNodePtr.p->failState == NORMAL);
 
+  /* Send API_FAILREQ to peer QMGR blocks to allow them to disconnect
+   * quickly
+   * Local application blocks get API_FAILREQ once all pending signals
+   * from the failed API have been processed.
+   */
+  signal->theData[0] = failedNodePtr.i;
+  signal->theData[1] = QMGR_REF;
+  NodeReceiverGroup rg(QMGR, c_clusterNodes);
+  sendSignal(rg, GSN_API_FAILREQ, signal, 2, JBA);
+  
+  /* Now ask CMVMI to disconnect the node */
+  FailState initialState = (failedNodePtr.p->phase == ZAPI_ACTIVE) ?
+    WAITING_FOR_CLOSECOMCONF_ACTIVE : 
+    WAITING_FOR_CLOSECOMCONF_NOTACTIVE;
+
+  failedNodePtr.p->failState = initialState;
   failedNodePtr.p->phase = ZFAIL_CLOSING;
   setNodeInfo(failedNodePtr.i).m_heartbeat_cnt= 0;
   setNodeInfo(failedNodePtr.i).m_version = 0;
@@ -2957,22 +2957,14 @@ Qmgr::api_failed(Signal* signal, Uint32 
   
   CloseComReqConf * const closeCom = (CloseComReqConf *)&signal->theData[0];
   closeCom->xxxBlockRef = reference();
+  closeCom->requestType = CloseComReqConf::RT_API_FAILURE;
   closeCom->failNo      = 0;
   closeCom->noOfNodes   = 1;
   NodeBitmask::clear(closeCom->theNodes);
   NodeBitmask::set(closeCom->theNodes, failedNodePtr.i);
   sendSignal(CMVMI_REF, GSN_CLOSE_COMREQ, signal, 
              CloseComReqConf::SignalLength, JBA);
-
-  if (getNodeInfo(failedNodePtr.i).getType() == NodeInfo::MGM)
-  {
-    /**
-     * Allow MGM do reconnect "directly"
-     */
-    jam();
-    setNodeInfo(failedNodePtr.i).m_heartbeat_cnt = 3;
-  }
-}
+} // api_failed
 
 /**--------------------------------------------------------------------------
  * AN API NODE IS REGISTERING. IF FOR THE FIRST TIME WE WILL ENABLE 
@@ -3502,6 +3494,80 @@ void Qmgr::execPREP_FAILREQ(Signal* sign
   return;
 }//Qmgr::execPREP_FAILREQ()
 
+
+void Qmgr::handleApiCloseComConf(Signal* signal)
+{
+  jam();
+  CloseComReqConf * const closeCom = (CloseComReqConf *)&signal->theData[0];
+
+  /* Api failure special case */
+  for(Uint32 nodeId = 0; nodeId < MAX_NDB_NODES; nodeId ++)
+  {
+    if(NdbNodeBitmask::get(closeCom->theNodes, nodeId))
+    {
+      jam();
+      /* Check that *only* 1 *API* node is included in
+       * this CLOSE_COM_CONF
+       */
+      ndbrequire(getNodeInfo(nodeId).getType() != NodeInfo::DB);
+      ndbrequire(closeCom->noOfNodes == 1);
+      NdbNodeBitmask::clear(closeCom->theNodes, nodeId);
+      ndbrequire(NdbNodeBitmask::isclear(closeCom->theNodes));
+      
+      /* Now that we know communication from the failed Api has
+       * ceased, we can send the required API_FAILREQ signals
+       * and continue API failure handling
+       */
+      NodeRecPtr failedNodePtr;
+      failedNodePtr.i = nodeId;
+      ptrCheckGuard(failedNodePtr, MAX_NODES, nodeRec);
+      
+      ndbrequire((failedNodePtr.p->failState == 
+                  WAITING_FOR_CLOSECOMCONF_ACTIVE) ||
+                 (failedNodePtr.p->failState ==
+                  WAITING_FOR_CLOSECOMCONF_NOTACTIVE));
+      
+      if (failedNodePtr.p->failState == WAITING_FOR_CLOSECOMCONF_ACTIVE)
+      {
+        /**
+         * Inform application blocks TC, DICT, SUMA etc.
+         */
+        jam();
+        sendApiFailReq(signal, nodeId);
+        arbitRec.code = ArbitCode::ApiFail;
+        handleArbitApiFail(signal, nodeId);
+      }
+      else
+      {
+        /**
+         * Always inform SUMA
+         * JBB used to ensure delivery *after* any pending
+         * signals.
+         */
+        jam();
+        signal->theData[0] = nodeId;
+        signal->theData[1] = QMGR_REF;
+        sendSignal(SUMA_REF, GSN_API_FAILREQ, signal, 2, JBB);
+        failedNodePtr.p->failState = WAITING_FOR_FAILCONF3;
+      }
+      
+      if (getNodeInfo(failedNodePtr.i).getType() == NodeInfo::MGM)
+      {
+        /**
+         * Allow MGM do reconnect "directly"
+         */
+        jam();
+        setNodeInfo(failedNodePtr.i).m_heartbeat_cnt = 3;
+      }
+      
+      /* Handled the single API node failure */
+      return;
+    }
+  }
+  /* Never get here */
+  ndbrequire(false);
+}
+
 /**---------------------------------------------------------------------------
  * THE CRASHED NODES HAS BEEN EXCLUDED FROM COMMUNICATION. 
  * WE WILL CHECK WHETHER ANY MORE NODES HAVE FAILED DURING THE PREPARE PROCESS.
@@ -3517,6 +3583,17 @@ void Qmgr::execCLOSE_COMCONF(Signal* sig
 
   CloseComReqConf * const closeCom = (CloseComReqConf *)&signal->theData[0];
 
+  Uint32 requestType = closeCom->requestType;
+
+  if (requestType == CloseComReqConf::RT_API_FAILURE)
+  {
+    jam();
+    handleApiCloseComConf(signal);
+    return;
+  }
+
+  /* Normal node failure preparation path */
+  ndbassert(requestType == CloseComReqConf::RT_NODE_FAILURE);
   BlockReference Tblockref  = closeCom->xxxBlockRef;
   Uint16 TfailureNr = closeCom->failNo;
 
@@ -4251,6 +4328,7 @@ void Qmgr::sendCloseComReq(Signal* signa
   CloseComReqConf * const closeCom = (CloseComReqConf *)&signal->theData[0];
   
   closeCom->xxxBlockRef = TBRef;
+  closeCom->requestType = CloseComReqConf::RT_NODE_FAILURE;
   closeCom->failNo      = aFailNo;
   closeCom->noOfNodes   = cnoPrepFailedNodes;
   

=== modified file 'storage/ndb/test/ndbapi/testNdbApi.cpp'
--- a/storage/ndb/test/ndbapi/testNdbApi.cpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/test/ndbapi/testNdbApi.cpp	2009-09-01 10:50:11 +0000
@@ -40,6 +40,14 @@
   result = NDBT_FAILED; \
   continue; } 
 
+static const char* ApiFailTestRun = "ApiFailTestRun";
+static const char* ApiFailTestComplete = "ApiFailTestComplete";
+static const char* ApiFailTestsRunning = "ApiFailTestsRunning";
+static const char* ApiFailNumberPkSteps = "ApiFailNumberPkSteps";
+static const int MAX_STEPS = 10;
+static Ndb_cluster_connection* otherConnection = NULL;
+static Ndb* stepNdbs[MAX_STEPS];
+
 
 int runTestMaxNdb(NDBT_Context* ctx, NDBT_Step* step){
   Uint32 loops = ctx->getNumLoops();
@@ -2843,6 +2851,259 @@ runBug44065(NDBT_Context* ctx, NDBT_Step
   return NDBT_OK;
 }
 
+int testApiFailReqImpl(NDBT_Context* ctx, NDBT_Step* step)
+{
+  /* Setup a separate connection for running PK updates
+   * with that will be disconnected without affecting
+   * the test framework
+   */
+  if (otherConnection != NULL)
+  {
+    ndbout << "Connection not null" << endl;
+    return NDBT_FAILED;
+  }
+  
+  const Uint32 MaxConnectStringSize= 256;
+  char connectString[ MaxConnectStringSize ];
+  ctx->m_cluster_connection.get_connectstring(connectString, 
+                                              MaxConnectStringSize);
+  
+  otherConnection= new Ndb_cluster_connection(connectString);
+  
+  if (otherConnection == NULL)
+  {
+    ndbout << "Connection is null" << endl;
+    return NDBT_FAILED;
+  }
+  
+  int rc= otherConnection->connect();
+  
+  if (rc!= 0)
+  {
+    ndbout << "Connect failed with rc " << rc << endl;
+    return NDBT_FAILED;
+  }
+  
+  /* Check that all nodes are alive - if one has failed
+   * then probably we exposed bad API_FAILREQ handling
+   */
+  if (otherConnection->wait_until_ready(10,10) != 0)
+  {
+    ndbout << "Cluster connection was not ready" << endl;
+    return NDBT_FAILED;
+  }
+  
+  for (int i=0; i < MAX_STEPS; i++)
+  {
+    /* We must create the Ndb objects here as we 
+     * are still single threaded
+     */
+    stepNdbs[i]= new Ndb(otherConnection,
+                         "TEST_DB");
+    stepNdbs[i]->init();
+    int rc= stepNdbs[i]->waitUntilReady(10);
+    
+    if (rc != 0)
+    {
+      ndbout << "Ndb " << i << " was not ready" << endl;
+      return NDBT_FAILED;
+    }
+    
+  }
+  
+  /* Now signal the 'worker' threads to start sending Pk
+   * reads
+   */
+  ctx->setProperty(ApiFailTestRun, 1);
+  
+  /* Wait until all of them are running before proceeding */
+  ctx->getPropertyWait(ApiFailTestsRunning, 
+                       ctx->getProperty(ApiFailNumberPkSteps));
+
+  if (ctx->isTestStopped())
+  {
+    return NDBT_OK;
+  }
+  
+  /* Clear the test-run flag so that they'll wait after
+   * they hit an error
+   */
+  ctx->setProperty(ApiFailTestRun, (Uint32)0);
+
+  /* Wait a little */
+  sleep(1);
+
+  /* Active more stringent checking of behaviour after
+   * API_FAILREQ
+   */
+  NdbRestarter restarter;
+    
+  /* Activate 8078 - TCs will abort() if they get a TCKEYREQ
+   * from the failed API after an API_FAILREQ message
+   */
+  ndbout << "Activating 8078" << endl;
+  restarter.insertErrorInAllNodes(8078);
+  
+  /* Wait a little longer */
+  sleep(1);
+  
+  /* Now cause our connection to disconnect
+   * This results in TC receiving an API_FAILREQ
+   * If there's an issue with API_FAILREQ 'cleanly'
+   * stopping further signals, there should be
+   * an assertion failure in TC 
+   */
+  int otherNodeId = otherConnection->node_id();
+  
+  ndbout << "Forcing disconnect of node " 
+         << otherNodeId << endl;
+  
+  /* All dump 900 <nodeId> */
+  int args[2]= {900, otherNodeId};
+  
+  restarter.dumpStateAllNodes( args, 2 );
+  
+
+  /* Now wait for all workers to finish
+   * (Running worker count to get down to zero
+   */
+  ctx->getPropertyWait(ApiFailTestsRunning, (Uint32)0);
+
+  if (ctx->isTestStopped())
+  {
+    return NDBT_OK;
+  }
+  
+  /* Clean up error insert */
+  restarter.insertErrorInAllNodes(0);
+  
+  /* Clean up allocated resources */
+  for (int i= 0; i < MAX_STEPS; i++)
+  {
+    delete stepNdbs[i];
+    stepNdbs[i]= NULL;
+  }
+  
+  delete otherConnection;
+  otherConnection= NULL;
+  
+  return NDBT_OK;
+}
+
+
+int testApiFailReq(NDBT_Context* ctx, NDBT_Step* step)
+{  
+  /* Perform a number of iterations, connecting,
+   * sending lots of PK updates, inserting error
+   * and then causing node failure
+   */
+  Uint32 iterations = 10;
+  int rc = NDBT_OK;
+
+  while (iterations --)
+  {
+    rc= testApiFailReqImpl(ctx, step);
+    
+    if (rc == NDBT_FAILED)
+    {
+      break;
+    }
+  } // while(iterations --)
+    
+  /* Avoid PkRead worker threads getting stuck */
+  ctx->setProperty(ApiFailTestComplete, (Uint32) 1);
+
+  return rc;
+}
+
+int runBulkPkReads(NDBT_Context* ctx, NDBT_Step* step)
+{
+  /* Run batched Pk reads */
+
+  while(true)
+  {
+    /* Wait to be signalled to start running */
+    while ((ctx->getProperty(ApiFailTestRun) == 0) &&
+           (ctx->getProperty(ApiFailTestComplete) == 0) &&
+           !ctx->isTestStopped())
+    {
+      ctx->wait_timeout(500); /* 500 millis */
+    }
+
+    if (ctx->isTestStopped() ||
+        (ctx->getProperty(ApiFailTestComplete) != 0))
+    {
+      /* Asked to stop by main test thread */
+      return NDBT_OK;
+    }
+    /* Indicate that we're underway */
+    ctx->incProperty(ApiFailTestsRunning);
+      
+    Ndb* otherNdb = stepNdbs[step->getStepNo()];
+    HugoOperations hugoOps(*ctx->getTab());
+    Uint32 numRecords = ctx->getNumRecords();
+    Uint32 batchSize = (1000 < numRecords)? 1000 : numRecords;
+    
+    ndbout << "Step number " << step->getStepNo()
+           << " reading batches of " << batchSize 
+           << " rows " << endl;
+    
+    while(true)
+    {
+      if (hugoOps.startTransaction(otherNdb) != 0)
+      {
+        ndbout << "Failed to start transaction.  Error : "
+               << otherNdb->getNdbError().message << endl;
+        return NDBT_FAILED;
+      }
+      
+      for (Uint32 op = 0; op < batchSize; op++)
+      {
+        if (hugoOps.pkReadRecord(otherNdb,
+                                 op) != 0)
+        {
+          ndbout << "Failed to define read of record number " << op << endl;
+          ndbout << "Error : " << hugoOps.getTransaction()->getNdbError().message 
+                 << endl;
+          return NDBT_FAILED;
+        }
+      }
+      
+      if (hugoOps.execute_Commit(otherNdb) != 0)
+      {
+        NdbError err = hugoOps.getTransaction()->getNdbError();
+        ndbout << "Execute failed with Error : " 
+               << err.message
+               << endl;
+        
+        hugoOps.closeTransaction(otherNdb);
+        
+        if ((err.code == 4010) || // Node failure
+            (err.code == 4025) || // Node failure
+            (err.code == 1218))   // Send buffer overload (reading larger tables)
+        {
+          /* Expected scenario due to injected Api disconnect 
+           * If there was a node failure due to assertion failure
+           * then we'll detect it when we try to setup a new
+           * connection
+           */
+          break; 
+        }
+        return NDBT_FAILED;
+      }
+      
+      hugoOps.closeTransaction(otherNdb);
+    }
+
+    /* Signal that we've finished running this iteration */
+    ctx->decProperty(ApiFailTestsRunning);
+  }
+ 
+  return NDBT_OK;
+}
+  
+  
+
 NDBT_TESTSUITE(testNdbApi);
 TESTCASE("MaxNdb", 
 	 "Create Ndb objects until no more can be created\n"){ 
@@ -2992,6 +3253,24 @@ TESTCASE("Bug44065",
          "Rollback no-change update on top of existing data") {
   INITIALIZER(runBug44065);
 }
+TESTCASE("ApiFailReqBehaviour",
+         "Check ApiFailReq cleanly marks Api disconnect") {
+  // Some flags to enable the various threads to cooperate
+  TC_PROPERTY(ApiFailTestRun, (Uint32)0);
+  TC_PROPERTY(ApiFailTestComplete, (Uint32)0);
+  TC_PROPERTY(ApiFailTestsRunning, (Uint32)0);
+  TC_PROPERTY(ApiFailNumberPkSteps, (Uint32)5); // Num threads below
+  INITIALIZER(runLoadTable);
+  // 5 threads to increase probability of pending
+  // TCKEYREQ after API_FAILREQ
+  STEP(runBulkPkReads);
+  STEP(runBulkPkReads);
+  STEP(runBulkPkReads);
+  STEP(runBulkPkReads);
+  STEP(runBulkPkReads);
+  STEP(testApiFailReq);
+  FINALIZER(runClearTable);
+}
 NDBT_TESTSUITE_END(testNdbApi);
 
 int main(int argc, const char** argv){

=== modified file 'storage/ndb/test/run-test/autotest-boot.sh'
--- a/storage/ndb/test/run-test/autotest-boot.sh	2008-12-16 17:12:00 +0000
+++ b/storage/ndb/test/run-test/autotest-boot.sh	2009-09-03 07:43:49 +0000
@@ -13,7 +13,12 @@ save_args=$*
 VERSION="autotest-boot.sh version 1.00"
 
 DATE=`date '+%Y-%m-%d'`
-HOST=`hostname -s`
+if [ `uname -s` != "SunOS" ]
+then
+  HOST=`hostname -s`
+else
+  HOST=`hostname`
+fi
 export DATE HOST
 
 set -e
@@ -220,13 +225,16 @@ fi
 ################################
 
 script=$install_dir0/mysql-test/ndb/autotest-run.sh
-sh -x $script $save_args --conf=$conf --run-dir=$install_dir --install-dir0=$install_dir0 --install-dir1=$install_dir1 --suite=$RUN --nolock $extra_args
+for R in $RUN
+do
+    sh -x $script $save_args --conf=$conf --run-dir=$install_dir --install-dir0=$install_dir0 --install-dir1=$install_dir1 --suite=$R --nolock $extra_args
+done
 
 if [ "$build" ]
 then
     rm -rf $dst_place0
 
-    if [ "$clone1" ]
+    if [ "$dst_place1" ]
     then
 	rm -rf $dst_place1
     fi

=== modified file 'storage/ndb/test/run-test/daily-basic-tests.txt'
--- a/storage/ndb/test/run-test/daily-basic-tests.txt	2009-08-21 13:05:47 +0000
+++ b/storage/ndb/test/run-test/daily-basic-tests.txt	2009-09-01 11:04:11 +0000
@@ -1304,3 +1304,8 @@ max-time: 300
 cmd: testDict
 args: -n Bug36702 D1
 
+# Test clean ApiFailReq behaviour
+max-time: 300
+cmd: testNdbApi
+args: -n ApiFailReqBehaviour T1
+

Thread
bzr commit into mysql-5.1-telco-6.2 branch (Martin.Skold:2973) Bug#38034Bug#46712 Bug#47039 Bug#47072Martin Skold7 Sep