List:Commits« Previous MessageNext Message »
From:Martin Skold Date:December 1 2009 2:22pm
Subject:bzr commit into mysql-5.1-telco-6.2 branch (Martin.Skold:3040) Bug#28116
Bug#48861 Bug#48910 Bug#48973
View as plain text  
#At file:///home/marty/MySQL/mysql-5.1-telco-6.2/

 3040 Martin Skold	2009-12-01 [merge]
      Merge
      modified:
        storage/ndb/src/kernel/blocks/ndbfs/AsyncFile.hpp
        storage/ndb/src/kernel/blocks/ndbfs/MemoryChannel.hpp
        storage/ndb/src/kernel/blocks/pgman.cpp
        storage/ndb/src/kernel/blocks/pgman.hpp
        storage/ndb/src/ndbapi/NdbOperationDefine.cpp
        storage/ndb/src/ndbapi/NdbOperationSearch.cpp
        storage/ndb/test/ndbapi/testBlobs.cpp
        storage/ndb/test/run-test/daily-basic-tests.txt

=== modified file 'storage/ndb/src/kernel/blocks/ndbfs/AsyncFile.hpp'
--- a/storage/ndb/src/kernel/blocks/ndbfs/AsyncFile.hpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/src/kernel/blocks/ndbfs/AsyncFile.hpp	2009-11-30 11:12:13 +0000
@@ -167,6 +167,8 @@ public:
    // Information for open, needed if the first open action fails.
   AsyncFile* file;
   Uint32 theTrace;
+
+  MemoryChannel<Request>::ListMember m_mem_channel;
 };
 
 NdbOut& operator <<(NdbOut&, const Request&);

=== modified file 'storage/ndb/src/kernel/blocks/ndbfs/MemoryChannel.hpp'
--- a/storage/ndb/src/kernel/blocks/ndbfs/MemoryChannel.hpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/src/kernel/blocks/ndbfs/MemoryChannel.hpp	2009-11-30 11:12:13 +0000
@@ -78,19 +78,26 @@ template <class T>
 class MemoryChannel
 {
 public:
-  MemoryChannel( int size= 512);
-  virtual ~MemoryChannel( );
+  MemoryChannel();
+  virtual ~MemoryChannel();
 
-  void writeChannel( T *t);
-  void writeChannelNoSignal( T *t);
+  void writeChannel(T *t);
+  void writeChannelNoSignal(T *t);
   T* readChannel();
   T* tryReadChannel();
 
+  /**
+   * Should be made class using MemoryChannel
+   */
+  struct ListMember
+  {
+    T* m_next;
+  };
+
 private:
-  int theSize;
-  T **theChannel;
-  CircularIndex theWriteIndex;
-  CircularIndex theReadIndex;
+  Uint32 m_occupancy;
+  T* m_head; // First element in list (e.g will be read by readChannel)
+  T* m_tail;
   NdbMutex* theMutexPtr;
   NdbCondition* theConditionPtr;
 
@@ -102,18 +109,14 @@ template <class T>
 NdbOut& operator<<(NdbOut& out, const MemoryChannel<T> & chn)
 {
   NdbMutex_Lock(chn.theMutexPtr);
-  out << "[ theSize: " << chn.theSize
-      << " theReadIndex: " << (int)chn.theReadIndex 
-      << " theWriteIndex: " << (int)chn.theWriteIndex << " ]";
+  out << "[ occupancy: " << chn.m_occupancy
+      << " ]";
   NdbMutex_Unlock(chn.theMutexPtr);
   return out;
 }
 
-template <class T> MemoryChannel<T>::MemoryChannel( int size):
-        theSize(size),
-        theChannel(new T*[size] ),
-        theWriteIndex(0, size),
-        theReadIndex(0, size)
+template <class T> MemoryChannel<T>::MemoryChannel() :
+  m_occupancy(0), m_head(0), m_tail(0)
 {
   theMutexPtr = NdbMutex_Create();
   theConditionPtr = NdbCondition_Create();
@@ -123,55 +126,79 @@ template <class T> MemoryChannel<T>::~Me
 {
   NdbMutex_Destroy(theMutexPtr);
   NdbCondition_Destroy(theConditionPtr);
-  delete [] theChannel;
 }
 
 template <class T> void MemoryChannel<T>::writeChannel( T *t)
 {
-
-  NdbMutex_Lock(theMutexPtr);
-  if(full(theWriteIndex, theReadIndex) || theChannel == NULL) abort();
-  theChannel[theWriteIndex]= t;
-  ++theWriteIndex;
-  NdbMutex_Unlock(theMutexPtr);
+  writeChannelNoSignal(t);
   NdbCondition_Signal(theConditionPtr);
 }
 
 template <class T> void MemoryChannel<T>::writeChannelNoSignal( T *t)
 {
-
   NdbMutex_Lock(theMutexPtr);
-  if(full(theWriteIndex, theReadIndex) || theChannel == NULL) abort();
-  theChannel[theWriteIndex]= t;
-  ++theWriteIndex;
+  if (m_head == 0)
+  {
+    assert(m_occupancy == 0);
+    m_head = m_tail = t;
+  }
+  else
+  {
+    assert(m_tail != 0);
+    m_tail->m_mem_channel.m_next = t;
+    m_tail = t;
+  }
+  t->m_mem_channel.m_next = 0;
+  m_occupancy++;
   NdbMutex_Unlock(theMutexPtr);
 }
 
 template <class T> T* MemoryChannel<T>::readChannel()
 {
-  T* tmp;
-
   NdbMutex_Lock(theMutexPtr);
-  while ( empty(theWriteIndex, theReadIndex) )
+  while (m_head == 0)
   {
+    assert(m_occupancy == 0);
     NdbCondition_Wait(theConditionPtr,
-                        theMutexPtr);    
+                      theMutexPtr);    
   }
-        
-  tmp= theChannel[theReadIndex];
-  ++theReadIndex;
+  assert(m_occupancy > 0);
+  T* tmp = m_head;
+  if (m_head == m_tail)
+  {
+    assert(m_occupancy == 1);
+    m_head = m_tail = 0;
+  }
+  else
+  {
+    m_head = m_head->m_mem_channel.m_next;
+  }
+  m_occupancy--;
   NdbMutex_Unlock(theMutexPtr);
   return tmp;
 }
 
 template <class T> T* MemoryChannel<T>::tryReadChannel()
 {
-  T* tmp= 0;
   NdbMutex_Lock(theMutexPtr);
-  if ( !empty(theWriteIndex, theReadIndex) )
-  {     
-    tmp= theChannel[theReadIndex];
-    ++theReadIndex;
+  T* tmp = m_head;
+  if (m_head != 0)
+  {
+    assert(m_occupancy > 0);
+    if (m_head == m_tail)
+    {
+      assert(m_occupancy == 1);
+      m_head = m_tail = 0;
+    }
+    else
+    {
+      m_head = m_head->m_mem_channel.m_next;
+    }
+    m_occupancy--;
+  }
+  else
+  {
+    assert(m_occupancy == 0);
   }
   NdbMutex_Unlock(theMutexPtr);
   return tmp;

=== modified file 'storage/ndb/src/kernel/blocks/pgman.cpp'
--- a/storage/ndb/src/kernel/blocks/pgman.cpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/src/kernel/blocks/pgman.cpp	2009-11-26 19:25:10 +0000
@@ -34,14 +34,6 @@
                      Page_request::DIRTY_REQ | \
                      Page_request::ALLOC_REQ)
 
-// todo use this
-#ifdef VM_TRACE
-#define dbg(x) \
-  do { if (! debugFlag) break; debugOut << "PGMAN: " << x << endl; } while (0)
-#else
-#define dbg(x)
-#endif
-
 static bool g_dbg_lcp = false;
 #if 1
 #define DBG_LCP(x)
@@ -58,6 +50,7 @@ Pgman::Pgman(Block_context& ctx) :
 #ifdef VM_TRACE
   ,debugOut(* new NullOutputStream())
   ,debugFlag(false)
+  ,debugSummaryFlag(false)
 #endif
 {
   BLOCK_CONSTRUCTOR(Pgman);
@@ -129,6 +122,7 @@ Pgman::execREAD_CONFIG_REQ(Signal* signa
     m_param.m_max_pages = page_buffer;
     m_page_entry_pool.setSize(m_param.m_lirs_stack_mult * page_buffer);
     m_param.m_max_hot_pages = (page_buffer * 9) / 10;
+    ndbrequire(m_param.m_max_hot_pages >= 1);
   }
 
   Pool_context pc;
@@ -156,9 +150,10 @@ Pgman::Param::Param() :
 
 Pgman::Stats::Stats() :
   m_num_pages(0),
+  m_num_hot_pages(0),
+  m_current_io_waits(0),
   m_page_hits(0),
-  m_page_faults(0),
-  m_current_io_waits(0)
+  m_page_faults(0)
 {
 }
 
@@ -280,10 +275,6 @@ Pgman::Page_entry::Page_entry(Uint32 fil
 Uint32
 Pgman::get_sublist_no(Page_state state)
 {
-  if (state == 0)
-  {
-    return ZNIL;
-  }
   if (state & Page_entry::REQUEST)
   {
     if (! (state & Page_entry::BOUND))
@@ -315,7 +306,11 @@ Pgman::get_sublist_no(Page_state state)
   if (state == Page_entry::ONSTACK) {
     return Page_entry::SL_IDLE;
   }
-  return Page_entry::SL_OTHER;
+  if (state != 0)
+  {
+    return Page_entry::SL_OTHER;
+  }
+  return ZNIL;
 }
 
 void
@@ -350,10 +345,25 @@ Pgman::set_page_state(Ptr<Page_entry> pt
       }
     }
     ptr.p->m_state = new_state;
+
+    bool old_hot = (old_state & Page_entry::HOT);
+    bool new_hot = (new_state & Page_entry::HOT);
+    if (! old_hot && new_hot)
+    {
+      jam();
+      m_stats.m_num_hot_pages++;
+    }
+    if (old_hot && ! new_hot)
+    {
+      jam();
+      ndbrequire(m_stats.m_num_hot_pages != 0);
+      m_stats.m_num_hot_pages--;
+    }
   }
 
 #ifdef VM_TRACE
   debugOut << "PGMAN: " << ptr << ": after" << endl;
+  verify_page_entry(ptr);
   debugOut << "PGMAN: <set_page_state" << endl;
 #endif
 }
@@ -635,8 +645,10 @@ Pgman::lirs_reference(Ptr<Page_entry> pt
   Page_state state = ptr.p->m_state;
   ndbrequire(! (state & Page_entry::LOCKED));
 
-  // even non-LIRS cache pages are counted on l.h.s.
-  if (m_stats.m_num_pages >= m_param.m_max_hot_pages)
+  ndbrequire(m_stats.m_num_hot_pages <= m_param.m_max_hot_pages);
+
+  // LIRS kicks in when we have max hot pages
+  if (m_stats.m_num_hot_pages == m_param.m_max_hot_pages)
   {
     if (state & Page_entry::HOT)
     {
@@ -678,6 +690,12 @@ Pgman::lirs_reference(Ptr<Page_entry> pt
       jam();
       pl_stack.add(ptr);
       state |= Page_entry::ONSTACK;
+      /*
+       * bug#48910.  Using hot page count (not total page count)
+       * guarantees that stack is not empty here.  Therefore the new
+       * entry (added to top) is not at bottom and need not be hot.
+       */
+      ndbrequire(pl_stack.hasPrev(ptr));
       if (state & Page_entry::ONQUEUE)
       {
         jam();
@@ -701,8 +719,8 @@ Pgman::lirs_reference(Ptr<Page_entry> pt
   else
   {
 #ifdef VM_TRACE
-    debugOut << "PGMAN: filling up initial hot pages: "
-             << m_stats.m_num_pages << " of "
+    debugOut << "PGMAN: filling up hot pages: "
+             << m_stats.m_num_hot_pages << "/"
              << m_param.m_max_hot_pages << endl;
 #endif
     jam();
@@ -1046,11 +1064,11 @@ Pgman::process_callback(Signal* signal, 
   debugOut << "PGMAN: " << ptr << " : process_callback" << endl;
 #endif
   int max_count = 1;
-  Page_state state = ptr.p->m_state;
 
   while (! ptr.p->m_requests.isEmpty() && --max_count >= 0)
   {
     jam();
+    Page_state state = ptr.p->m_state;
     SimulatedBlock* b;
     Callback callback;
     {
@@ -1092,19 +1110,18 @@ Pgman::process_callback(Signal* signal, 
     }
     ndbrequire(state & Page_entry::BOUND);
     ndbrequire(state & Page_entry::MAPPED);
+
+    // make REQUEST state consistent before set_page_state()
+    if (ptr.p->m_requests.isEmpty())
+    {
+      jam();
+      state &= ~ Page_entry::REQUEST;
+    }
     
     // callback may re-enter PGMAN and change page state
     set_page_state(ptr, state);
     b->execute(signal, callback, ptr.p->m_real_page_i);
-    state = ptr.p->m_state;
-  }
-  
-  if (ptr.p->m_requests.isEmpty())
-  {
-    jam();
-    state &= ~ Page_entry::REQUEST;
   }
-  set_page_state(ptr, state);
   return true;
 }
 
@@ -1657,7 +1674,7 @@ Pgman::execFSWRITEREF(Signal* signal)
 // client methods
 
 int
-Pgman::get_page(Signal* signal, Ptr<Page_entry> ptr, Page_request page_req)
+Pgman::get_page_no_lirs(Signal* signal, Ptr<Page_entry> ptr, Page_request page_req)
 {
   jamEntry();
 #ifdef VM_TRACE
@@ -1698,16 +1715,6 @@ Pgman::get_page(Signal* signal, Ptr<Page
     jam();
   }
 
-  // update LIRS
-  if (! (state & Page_entry::LOCKED) &&
-      ! (req_flags & Page_request::CORR_REQ))
-  {
-    jam();
-    set_page_state(ptr, state);
-    lirs_reference(ptr);
-    state = ptr.p->m_state;
-  }
-
   const Page_state LOCKED = Page_entry::LOCKED | Page_entry::MAPPED;
   if ((state & LOCKED) == LOCKED && 
       ! (req_flags & Page_request::UNLOCK_PAGE))
@@ -1794,8 +1801,6 @@ Pgman::get_page(Signal* signal, Ptr<Page
   ptr.p->m_busy_count += busy_count;
   ptr.p->m_dirty_count += !!(req_flags & DIRTY_FLAGS);
   set_page_state(ptr, state);
-  
-  do_busy_loop(signal, true);
 
 #ifdef VM_TRACE
   debugOut << "PGMAN: " << req_ptr << endl;
@@ -1804,6 +1809,37 @@ Pgman::get_page(Signal* signal, Ptr<Page
   return 0;
 }
 
+int
+Pgman::get_page(Signal* signal, Ptr<Page_entry> ptr, Page_request page_req)
+{
+  int i = get_page_no_lirs(signal, ptr, page_req);
+  if (unlikely(i == -1))
+  {
+    jam();
+    return -1;
+  }
+
+  Uint32 req_flags = page_req.m_flags;
+  Page_state state = ptr.p->m_state;
+
+  // update LIRS
+  if (! (state & Page_entry::LOCKED) &&
+      ! (req_flags & Page_request::CORR_REQ))
+  {
+    jam();
+    lirs_reference(ptr);
+  }
+
+  // start processing if request was queued
+  if (i == 0)
+  {
+    jam();
+    do_busy_loop(signal, true);
+  }
+
+  return i;
+}
+
 void
 Pgman::update_lsn(Ptr<Page_entry> ptr, Uint32 block, Uint64 lsn)
 {
@@ -1929,9 +1965,6 @@ Pgman::drop_page(Ptr<Page_entry> ptr)
   Page_state state = ptr.p->m_state;
   if (! (state & (Page_entry::PAGEIN | Page_entry::PAGEOUT)))
   {
-    ndbrequire(state & Page_entry::BOUND);
-    ndbrequire(state & Page_entry::MAPPED);
-
     if (state & Page_entry::ONSTACK)
     {
       jam();
@@ -1941,9 +1974,13 @@ Pgman::drop_page(Ptr<Page_entry> ptr)
       if (at_bottom)
       {
         jam();
-        ndbassert(state & Page_entry::HOT);
         lirs_stack_prune();
       }
+      if (state & Page_entry::HOT)
+      {
+        jam();
+        state &= ~ Page_entry::HOT;
+      }
     }
 
     if (state & Page_entry::ONQUEUE)
@@ -1953,12 +1990,37 @@ Pgman::drop_page(Ptr<Page_entry> ptr)
       state &= ~ Page_entry::ONQUEUE;
     }
 
-    ndbassert(ptr.p->m_real_page_i != RNIL);
-    if (ptr.p->m_real_page_i != RNIL)
+    if (state & Page_entry::BUSY)
+    {
+      jam();
+      state &= ~ Page_entry::BUSY;
+    }
+
+    if (state & Page_entry::DIRTY)
+    {
+      jam();
+      state &= ~ Page_entry::DIRTY;
+    }
+
+    if (state & Page_entry::EMPTY)
+    {
+      jam();
+      state &= ~ Page_entry::EMPTY;
+    }
+
+    if (state & Page_entry::MAPPED)
     {
       jam();
+      state &= ~ Page_entry::MAPPED;
+    }
+
+    if (state & Page_entry::BOUND)
+    {
+      jam();
+      ndbrequire(ptr.p->m_real_page_i != RNIL);
       release_cache_page(ptr.p->m_real_page_i);
       ptr.p->m_real_page_i = RNIL;
+      state &= ~ Page_entry::BOUND;
     }
 
     set_page_state(ptr, state);
@@ -1977,6 +2039,8 @@ Pgman::drop_page(Ptr<Page_entry> ptr)
 void
 Pgman::verify_page_entry(Ptr<Page_entry> ptr)
 {
+  Page_stack& pl_stack = m_page_stack;
+
   Uint32 ptrI = ptr.i;
   Page_state state = ptr.p->m_state;
 
@@ -1999,6 +2063,10 @@ Pgman::verify_page_entry(Ptr<Page_entry>
   // hot entry must be on stack
   ndbrequire(! is_hot || on_stack || dump_page_lists(ptrI));
 
+  // stack bottom is hot
+  bool at_bottom = on_stack && ! pl_stack.hasPrev(ptr);
+  ndbrequire(! at_bottom || is_hot || dump_page_lists(ptrI));
+
   bool on_queue = state & Page_entry::ONQUEUE;
   // hot entry is not on queue
   ndbrequire(! is_hot || ! on_queue || dump_page_lists(ptrI));
@@ -2010,9 +2078,12 @@ Pgman::verify_page_entry(Ptr<Page_entry>
   // entries waiting to enter queue
   bool to_queue = ! is_locked && ! is_hot && ! is_bound && has_req;
 
-  // page is either LOCKED or under LIRS
+  // page is about to be released
+  bool to_release = (state == 0);
+
+  // page is either LOCKED or under LIRS or about to be released
   bool is_lirs = on_stack || to_queue || on_queue;
-  ndbrequire(is_locked == ! is_lirs || dump_page_lists(ptrI));
+  ndbrequire(to_release || is_locked == ! is_lirs || dump_page_lists(ptrI));
 
   bool pagein = state & Page_entry::PAGEIN;
   bool pageout = state & Page_entry::PAGEOUT;
@@ -2044,6 +2115,9 @@ Pgman::verify_page_entry(Ptr<Page_entry>
     break;
   case Page_entry::SL_OTHER:
     break;
+  case ZNIL:
+    ndbrequire(to_release || dump_page_lists(ptrI));
+    break;
   default:
     ndbrequire(false || dump_page_lists(ptrI));
     break;
@@ -2053,122 +2127,125 @@ Pgman::verify_page_entry(Ptr<Page_entry>
 void
 Pgman::verify_page_lists()
 {
+  const Stats& stats = m_stats;
+  const Param& param = m_param;
   Page_hashlist& pl_hash = m_page_hashlist;
   Page_stack& pl_stack = m_page_stack;
   Page_queue& pl_queue = m_page_queue;
   Ptr<Page_entry> ptr;
 
-  Uint32 stack_count = 0;
-  Uint32 queue_count = 0;
-  Uint32 queuewait_count = 0;
-  Uint32 locked_bound_count = 0;
+  Uint32 is_locked = 0;
+  Uint32 is_bound = 0;
+  Uint32 is_mapped = 0;
+  Uint32 is_hot = 0;
+  Uint32 on_stack = 0;
+  Uint32 on_queue = 0;
+  Uint32 to_queue = 0;
 
   Page_hashlist::Iterator iter;
   pl_hash.next(0, iter);
   while (iter.curr.i != RNIL)
   {
-    verify_page_entry(iter.curr);
+    ptr = iter.curr;
+    Page_state state = ptr.p->m_state;
+    // (state == 0) occurs only within a time-slice
+    ndbrequire(state != 0);
+    verify_page_entry(ptr);
 
-    Page_state state = iter.curr.p->m_state;
+    if (state & Page_entry::LOCKED)
+      is_locked++;
+    if (state & Page_entry::BOUND)
+      is_bound++;
+    if (state & Page_entry::MAPPED)
+      is_mapped++;
+    if (state & Page_entry::HOT)
+      is_hot++;
     if (state & Page_entry::ONSTACK)
-      stack_count++;
+      on_stack++;
     if (state & Page_entry::ONQUEUE)
-      queue_count++;
+      on_queue++;
     if (! (state & Page_entry::LOCKED) &&
         ! (state & Page_entry::HOT) &&
         (state & Page_entry::REQUEST) &&
         ! (state & Page_entry::BOUND))
-      queuewait_count++;
-    if (state & Page_entry::LOCKED &&
-        state & Page_entry::BOUND)
-      locked_bound_count++;
+      to_queue++;
     pl_hash.next(iter);
   }
 
-  ndbrequire(stack_count == pl_stack.count() || dump_page_lists());
-  ndbrequire(queue_count == pl_queue.count() || dump_page_lists());
-
-  Uint32 hot_count = 0;
-  Uint32 hot_bound_count = 0;
-  Uint32 cold_bound_count = 0;
-  Uint32 stack_request_count = 0;
-  Uint32 queue_request_count = 0;
-
-  Uint32 i1 = RNIL;
   for (pl_stack.first(ptr); ptr.i != RNIL; pl_stack.next(ptr))
   {
-    ndbrequire(i1 != ptr.i);
-    i1 = ptr.i;
     Page_state state = ptr.p->m_state;
-    ndbrequire(state & Page_entry::ONSTACK || dump_page_lists());
+    ndbrequire(state & Page_entry::ONSTACK || dump_page_lists(ptr.i));
     if (! pl_stack.hasPrev(ptr))
-      ndbrequire(state & Page_entry::HOT || dump_page_lists());
-    if (state & Page_entry::HOT) {
-      hot_count++;
-      if (state & Page_entry::BOUND)
-        hot_bound_count++;
-    }
-    if (state & Page_entry::REQUEST)
-      stack_request_count++;
+      ndbrequire(state & Page_entry::HOT || dump_page_lists(ptr.i));
   }
 
-  Uint32 i2 = RNIL;
   for (pl_queue.first(ptr); ptr.i != RNIL; pl_queue.next(ptr))
   {
-    ndbrequire(i2 != ptr.i);
-    i2 = ptr.i;
     Page_state state = ptr.p->m_state;
-    ndbrequire(state & Page_entry::ONQUEUE || dump_page_lists());
-    ndbrequire(state & Page_entry::BOUND || dump_page_lists());
-    cold_bound_count++;
-    if (state & Page_entry::REQUEST)
-      queue_request_count++;
+    ndbrequire(state & Page_entry::ONQUEUE || dump_page_lists(ptr.i));
+    ndbrequire(state & Page_entry::BOUND || dump_page_lists(ptr.i));
+    ndbrequire(! (state & Page_entry::HOT) || dump_page_lists(ptr.i));
   }
 
-  Uint32 tot_bound_count =
-    locked_bound_count + hot_bound_count + cold_bound_count;
-  ndbrequire(m_stats.m_num_pages == tot_bound_count || dump_page_lists());
+  ndbrequire(is_bound == stats.m_num_pages || dump_page_lists());
+  ndbrequire(is_hot == stats.m_num_hot_pages || dump_page_lists());
+  ndbrequire(on_stack == pl_stack.count() || dump_page_lists());
+  ndbrequire(on_queue == pl_queue.count() || dump_page_lists());
 
   Uint32 k;
   Uint32 entry_count = 0;
-
+  char sublist_info[200] = "";
   for (k = 0; k < Page_entry::SUBLIST_COUNT; k++)
   {
     const Page_sublist& pl = *m_page_sublist[k];
     for (pl.first(ptr); ptr.i != RNIL; pl.next(ptr))
-    {
-      ndbrequire(get_sublist_no(ptr.p->m_state) == k || dump_page_lists());
-      entry_count++;
-    }
+      ndbrequire(get_sublist_no(ptr.p->m_state) == k || dump_page_lists(ptr.i));
+    entry_count += pl.count();
+    sprintf(sublist_info + strlen(sublist_info),
+            " %s:%u", get_sublist_name(k), pl.count());
   }
-
   ndbrequire(entry_count == pl_hash.count() || dump_page_lists());
 
-  debugOut << "PGMAN: loop"
-           << " stats=" << m_stats_loop_on
-           << " busy=" << m_busy_loop_on
-           << " cleanup=" << m_cleanup_loop_on
-           << " lcp=" << m_lcp_loop_on << endl;
-
-  debugOut << "PGMAN:"
-           << " entry:" << pl_hash.count()
-           << " cache:" << m_stats.m_num_pages
-           << "(" << locked_bound_count << "L)"
-           << " stack:" << pl_stack.count()
-           << " hot:" << hot_count
-           << " hot_bound:" << hot_bound_count
-           << " stack_request:" << stack_request_count
-           << " queue:" << pl_queue.count()
-           << " queue_request:" << queue_request_count
-           << " queuewait:" << queuewait_count << endl;
+  Uint32 hit_pct = 0;
+  char hit_pct_str[20];
+  if (stats.m_page_hits + stats.m_page_faults != 0)
+    hit_pct = 10000 * stats.m_page_hits /
+              (stats.m_page_hits + stats.m_page_faults);
+  sprintf(hit_pct_str, "%u.%02u", hit_pct / 100, hit_pct % 100);
 
-  debugOut << "PGMAN:";
-  for (k = 0; k < Page_entry::SUBLIST_COUNT; k++)
-  {
-    const Page_sublist& pl = *m_page_sublist[k];
-    debugOut << " " << get_sublist_name(k) << ":" << pl.count();
-  }
-  debugOut << endl;
+  if (! debugFlag && debugSummaryFlag)
+    open_debug_file(1);
+
+  debugOut
+    << "PGMAN: loop"
+    << " stats:" << m_stats_loop_on
+    << " busy:" << m_busy_loop_on
+    << " cleanup:" << m_cleanup_loop_on
+    << " lcp:" << m_lcp_loop_on << endl;
+
+  debugOut
+    << "PGMAN:"
+    << " entries:" << pl_hash.count()
+    << " pages:" << stats.m_num_pages << "/" << param.m_max_pages
+    << " mapped:" << is_mapped
+    << " hot:" << is_hot
+    << " io:" << stats.m_current_io_waits << "/" << param.m_max_io_waits
+    << " hit pct:" << hit_pct_str << endl;
+
+  debugOut
+    << "PGMAN:"
+    << " locked:" << is_locked
+    << " stack:" << pl_stack.count()
+    << " queue:" << pl_queue.count()
+    << " to queue:" << to_queue << endl;
+
+  debugOut
+    << "PGMAN:"
+    << sublist_info << endl;
+
+  if (! debugFlag && debugSummaryFlag)
+    open_debug_file(0);
 }
 
 void
@@ -2191,56 +2268,37 @@ Pgman::dump_page_lists(Uint32 ptrI)
   if (! debugFlag)
     open_debug_file(1);
 
-  debugOut << "PGMAN: page list dump" << endl;
+  debugOut << "PGMAN: page list dump" << "\n";
   if (ptrI != RNIL)
-    debugOut << "PGMAN: error on PE [" << ptrI << "]" << endl;
+    debugOut << "PGMAN: error on PE [" << ptrI << "]" << "\n";
 
   Page_hashlist& pl_hash = m_page_hashlist;
   Page_stack& pl_stack = m_page_stack;
   Page_queue& pl_queue = m_page_queue;
   Ptr<Page_entry> ptr;
   Uint32 n;
-  char buf[40];
 
-  debugOut << "hash:" << endl;
-  Page_hashlist::Iterator iter;
-  pl_hash.next(0, iter);
-  n = 0;
-  while (iter.curr.i != RNIL)
-  {
-    sprintf(buf, "%03d", n++);
-    debugOut << buf << " " << iter.curr << endl;
-    pl_hash.next(iter);
-  }
-
-  debugOut << "stack:" << endl;
+  debugOut << "stack:" << "\n";
   n = 0;
   for (pl_stack.first(ptr); ptr.i != RNIL; pl_stack.next(ptr))
-  {
-    sprintf(buf, "%03d", n++);
-    debugOut << buf << " " << ptr << endl;
-  }
+    debugOut << n++ << " " << ptr << "\n";
 
-  debugOut << "queue:" << endl;
+  debugOut << "queue:" << "\n";
   n = 0;
   for (pl_queue.first(ptr); ptr.i != RNIL; pl_queue.next(ptr))
-  {
-    sprintf(buf, "%03d", n++);
-    debugOut << buf << " " << ptr << endl;
-  }
+    debugOut << n++ << " " << ptr << "\n";
 
   Uint32 k;
   for (k = 0; k < Page_entry::SUBLIST_COUNT; k++)
   {
-    debugOut << get_sublist_name(k) << ":" << endl;
+    debugOut << get_sublist_name(k) << ":" << "\n";
     const Page_sublist& pl = *m_page_sublist[k];
+    n = 0;
     for (pl.first(ptr); ptr.i != RNIL; pl.next(ptr))
-    {
-      sprintf(buf, "%03d", n++);
-    debugOut << buf << " " << ptr << endl;
-    }
+      debugOut << n++ << " " << ptr << "\n";
   }
 
+  debugOut.flushline();
   if (! debugFlag)
     open_debug_file(0);
 
@@ -2260,9 +2318,9 @@ Pgman::get_sublist_name(Uint32 list_no)
   case Page_entry::SL_MAP_IO:
     return "map_io";
   case Page_entry::SL_CALLBACK:
-    return "callback";
+    return "cb";
   case Page_entry::SL_CALLBACK_IO:
-    return "callback_io";
+    return "cb_io";
   case Page_entry::SL_BUSY:
     return "busy";
   case Page_entry::SL_LOCKED:
@@ -2407,8 +2465,9 @@ Pgman::execDUMP_STATE_ORD(Signal* signal
   if (signal->theData[0] == 11000 && signal->getLength() == 2)
   {
     Uint32 flag = signal->theData[1];
-    open_debug_file(flag);
-    debugFlag = flag;
+    debugFlag = flag & 1;
+    debugSummaryFlag = flag & 2;
+    open_debug_file(debugFlag);
   }
 #endif
 

=== modified file 'storage/ndb/src/kernel/blocks/pgman.hpp'
--- a/storage/ndb/src/kernel/blocks/pgman.hpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/src/kernel/blocks/pgman.hpp	2009-11-26 19:24:32 +0000
@@ -54,7 +54,7 @@
  * Updating a resident cache page makes it "dirty".  A background
  * clean-up process makes dirty pages "clean" via "pageout" to disk.
  * Write ahead logging (WAL) of the page is done first i.e. UNDO log is
- * flushed up to the page log sequence number (LSN) by calling a TSMAN
+ * flushed up to the page log sequence number (LSN) by calling a LGMAN
  * method.  The reason for this is obvious but not relevant to PGMAN.
  *
  * A local check point (LCP) periodically performs a complete pageout of
@@ -418,9 +418,10 @@ private:
   struct Stats {
     Stats();
     Uint32 m_num_pages;         // current number of cache pages
-    Uint32 m_page_hits;
-    Uint32 m_page_faults;
+    Uint32 m_num_hot_pages;
     Uint32 m_current_io_waits;
+    Uint64 m_page_hits;
+    Uint64 m_page_faults;
   } m_stats;
 
 protected:
@@ -482,6 +483,7 @@ private:
   void fswritereq(Signal*, Ptr<Page_entry>);
   void fswriteconf(Signal*, Ptr<Page_entry>);
 
+  int get_page_no_lirs(Signal*, Ptr<Page_entry>, Page_request page_req);
   int get_page(Signal*, Ptr<Page_entry>, Page_request page_req);
   void update_lsn(Ptr<Page_entry>, Uint32 block, Uint64 lsn);
   Uint32 create_data_file();
@@ -493,6 +495,7 @@ private:
 #ifdef VM_TRACE
   NdbOut debugOut;
   bool debugFlag;
+  bool debugSummaryFlag; // loop summary to signal log even if ! debugFlag
   void verify_page_entry(Ptr<Page_entry> ptr);
   void verify_page_lists();
   void verify_all();

=== modified file 'storage/ndb/src/ndbapi/NdbOperationDefine.cpp'
--- a/storage/ndb/src/ndbapi/NdbOperationDefine.cpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/src/ndbapi/NdbOperationDefine.cpp	2009-11-27 13:17:22 +0000
@@ -698,6 +698,26 @@ NdbOperation::getBlobHandle(NdbTransacti
     return NULL;
   }
 
+  /* Check key fully defined for key operations */
+  switch (theStatus)
+  {
+  case TupleKeyDefined:
+  case GetValue:
+  case SetValue:
+  case FinalGetValue:
+  case ExecInterpretedValue:
+  case SetValueInterpreted:
+    /* All ok states to create a Blob Handle in */
+    break;
+  default:
+  {
+    /* Unexpected state to be obtaining Blob handle */
+    /* Invalid usage of blob attribute */
+    setErrorCodeAbort(4264);
+    return NULL;
+  }
+  }
+
   tBlob = theNdb->getNdbBlob();
   if (tBlob == NULL)
     return NULL;

=== modified file 'storage/ndb/src/ndbapi/NdbOperationSearch.cpp'
--- a/storage/ndb/src/ndbapi/NdbOperationSearch.cpp	2009-05-26 18:53:34 +0000
+++ b/storage/ndb/src/ndbapi/NdbOperationSearch.cpp	2009-11-27 13:13:26 +0000
@@ -473,7 +473,8 @@ NdbOperation::reorderKEYINFO()
 {
   Uint32 data[ NDB_MAX_KEYSIZE_IN_WORDS ];
   Uint32 size = NDB_MAX_KEYSIZE_IN_WORDS;
-  getKeyFromTCREQ(data, size);
+  int rc = getKeyFromTCREQ(data, size);
+  assert(rc == 0);
   Uint32 pos = 1;
   Uint32 k;
   for (k = 0; k < m_accessTable->m_noOfKeys; k++) {
@@ -505,7 +506,10 @@ NdbOperation::reorderKEYINFO()
 int
 NdbOperation::getKeyFromTCREQ(Uint32* data, Uint32 & size)
 {
-  assert(size >= theTupKeyLen && theTupKeyLen > 0);
+  /* Check that we can correctly return a valid key */
+  if ((size < theTupKeyLen) || (theTupKeyLen == 0))
+    return -1;
+  
   size = theTupKeyLen;
   unsigned pos = 0;
   while (pos < 8 && pos < size) {

=== modified file 'storage/ndb/test/ndbapi/testBlobs.cpp'
--- a/storage/ndb/test/ndbapi/testBlobs.cpp	2009-10-26 20:02:17 +0000
+++ b/storage/ndb/test/ndbapi/testBlobs.cpp	2009-11-27 13:17:22 +0000
@@ -3997,6 +3997,87 @@ bugtest_27370()
   return 0;
 }
 
+static int
+bugtest_28116()
+{
+  DBG("bug test 28116 - Crash in getBlobHandle() when called without full key");
+
+  if (g_opt.m_pk2chr.m_len == 0)
+  {
+    DBG("  ... skipped, requires multi-column primary key.");
+    return 0;
+  }
+
+  for (unsigned k = 0; k < g_opt.m_rows; k++) {
+    Tup& tup = g_tups[k];
+    CHK((g_con = g_ndb->startTransaction()) != 0);
+    CHK((g_opr = g_con->getNdbOperation(g_opt.m_tname)) != 0);
+    int reqType = urandom(4);
+    switch(reqType) {
+    case 0:
+    {
+      DBG("Read");
+      CHK(g_opr->readTuple() == 0);
+      break;
+    }
+    case 1:
+    {
+      DBG("Insert");
+      CHK(g_opr->insertTuple() == 0);
+      break;
+    }
+    case 2:
+    {
+      DBG("Update");
+      CHK(g_opr->updateTuple() == 0);
+      break;
+    }
+    case 3:
+    default:
+    {
+      DBG("Delete");
+      CHK(g_opr->deleteTuple() == 0);
+      break;
+    }
+    }
+    switch (urandom(3)) {
+    case 0:
+    {
+      DBG("  No keys");
+      break;
+    }
+    case 1:
+    {
+      DBG("  Pk1 only");
+      CHK(g_opr->equal("PK1", tup.m_pk1) == 0);
+      break;
+    }
+    case 2:
+    default:
+    {
+      DBG("  Pk2/3 only");
+      if (g_opt.m_pk2chr.m_len != 0)
+      {
+        CHK(g_opr->equal("PK2", tup.m_pk2) == 0);
+        CHK(g_opr->equal("PK3", tup.m_pk3) == 0);
+      }
+      break;
+    }
+    }
+    /* Deliberately no equal() on rest of primary key, to provoke error. */
+    CHK(g_opr->getBlobHandle("BL1") == 0);
+
+    /* 4264 - Invalid usage of Blob attribute */
+    CHK(g_con->getNdbError().code == 4264);
+    CHK(g_opr->getNdbError().code == 4264);
+
+    g_ndb->closeTransaction(g_con);
+    g_opr = 0;
+    g_con = 0;
+  }
+  return 0;
+}
+
 static struct {
   int m_bug;
   int (*m_test)();
@@ -4006,7 +4087,8 @@ static struct {
   { 27370, bugtest_27370 },
   { 36756, bugtest_36756 },
   { 45768, bugtest_45768 },
-  { 48040, bugtest_48040 }
+  { 48040, bugtest_48040 },
+  { 28116, bugtest_28116 }
 };
 
 NDB_COMMAND(testOdbcDriver, "testBlobs", "testBlobs", "testBlobs", 65535)

=== modified file 'storage/ndb/test/run-test/daily-basic-tests.txt'
--- a/storage/ndb/test/run-test/daily-basic-tests.txt	2009-10-26 20:02:17 +0000
+++ b/storage/ndb/test/run-test/daily-basic-tests.txt	2009-11-27 13:17:22 +0000
@@ -1378,3 +1378,7 @@ max-time: 300
 cmd: testBasic
 args: -n DDInsertFailUpdateBatch D1 D2
 
+max-time: 300
+cmd: testBlobs
+args: -skip hp -bug 28116
+

Thread
bzr commit into mysql-5.1-telco-6.2 branch (Martin.Skold:3040) Bug#28116Bug#48861 Bug#48910 Bug#48973Martin Skold1 Dec