[CalendarServer-changes] [9249] CalendarServer/trunk/twext

source_changes at macosforge.org source_changes at macosforge.org
Wed May 16 07:35:03 PDT 2012


Revision: 9249
          http://trac.macosforge.org/projects/calendarserver/changeset/9249
Author:   cdaboo at apple.com
Date:     2012-05-16 07:35:02 -0700 (Wed, 16 May 2012)
Log Message:
-----------
Handle issues with incorrect tracking of busy status of child processes.

Modified Paths:
--------------
    CalendarServer/trunk/twext/internet/sendfdport.py
    CalendarServer/trunk/twext/internet/test/test_sendfdport.py
    CalendarServer/trunk/twext/web2/metafd.py
    CalendarServer/trunk/twext/web2/test/test_metafd.py

Modified: CalendarServer/trunk/twext/internet/sendfdport.py
===================================================================
--- CalendarServer/trunk/twext/internet/sendfdport.py	2012-05-15 15:38:47 UTC (rev 9248)
+++ CalendarServer/trunk/twext/internet/sendfdport.py	2012-05-16 14:35:02 UTC (rev 9249)
@@ -122,7 +122,7 @@
         Receive a status / health message and record it.
         """
         try:
-            data, flags, ancillary = recvmsg(self.skt.fileno())
+            data, _ignore_flags, _ignore_ancillary = recvmsg(self.skt.fileno())
         except SocketError, se:
             if se.errno not in (EAGAIN, ENOBUFS):
                 raise
@@ -192,7 +192,11 @@
         @param description: some text to identify to the subprocess's
             L{InheritedPort} what type of transport to create for this socket.
         """
-        self._subprocessSockets.sort(key=lambda conn: conn.status)
+        
+        # We want None to sort after 0 and before 1, so coerce to 0.5 - this allows the master
+        # to first schedule all child process that are up but not yet busy ahead of those that
+        # are still starting up.
+        self._subprocessSockets.sort(key=lambda conn: 0.5 if conn.status is None else conn.status)
         selectedSocket = self._subprocessSockets[0]
         selectedSocket.sendSocketToPeer(skt, description)
         # XXX Maybe want to send along 'description' or 'skt' or some

Modified: CalendarServer/trunk/twext/internet/test/test_sendfdport.py
===================================================================
--- CalendarServer/trunk/twext/internet/test/test_sendfdport.py	2012-05-15 15:38:47 UTC (rev 9248)
+++ CalendarServer/trunk/twext/internet/test/test_sendfdport.py	2012-05-16 14:35:02 UTC (rev 9249)
@@ -1,6 +1,6 @@
 # -*- test-case-name: twext.internet.test.test_sendfdport -*-
 ##
-# Copyright (c) 2010 Apple Inc. All rights reserved.
+# Copyright (c) 2010-2012 Apple Inc. All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -19,9 +19,11 @@
 Tests for L{twext.internet.sendfdport}.
 """
 
+from twext.internet.sendfdport import InheritedSocketDispatcher,\
+    _SubprocessSocket
+from twext.web2.metafd import ConnectionLimiter
+from twisted.internet.interfaces import IReactorFDSet
 from twisted.trial.unittest import TestCase
-from twext.internet.sendfdport import InheritedSocketDispatcher
-from twisted.internet.interfaces import IReactorFDSet
 from zope.interface.declarations import implements
 
 
@@ -55,3 +57,54 @@
         dispatcher.startDispatching()
         dispatcher.addSocket()
         self.assertEquals(reactor.getReaders(), dispatcher._subprocessSockets)
+
+
+    def test_sendFileDescriptorSorting(self):
+        """
+        Make sure InheritedSocketDispatcher.sendFileDescriptor sorts sockets with status None
+        higher than those with int status values.
+        """
+        
+        self.patch(_SubprocessSocket, 'sendSocketToPeer', lambda x, y, z:None)
+        dispatcher = InheritedSocketDispatcher(ConnectionLimiter(2, 20))
+        dispatcher.addSocket()
+        dispatcher.addSocket()
+        dispatcher.addSocket()
+
+        sockets = dispatcher._subprocessSockets[:]
+        
+        # Check that 0 is preferred over None
+        sockets[0].status = 0
+        sockets[1].status = 1
+        sockets[2].status = None
+        
+        dispatcher.sendFileDescriptor(None, "")
+        
+        self.assertEqual(sockets[0].status, 1)
+        self.assertEqual(sockets[1].status, 1)
+        self.assertEqual(sockets[2].status, None)
+        
+        dispatcher.sendFileDescriptor(None, "")
+        
+        self.assertEqual(sockets[0].status, 1)
+        self.assertEqual(sockets[1].status, 1)
+        self.assertEqual(sockets[2].status, 1)
+
+        # Check that after going to 1 and back to 0 that is still preferred over None 
+        sockets[0].status = 0
+        sockets[1].status = 1
+        sockets[2].status = None
+        
+        dispatcher.sendFileDescriptor(None, "")
+        
+        self.assertEqual(sockets[0].status, 1)
+        self.assertEqual(sockets[1].status, 1)
+        self.assertEqual(sockets[2].status, None)
+        
+        sockets[1].status = 0
+
+        dispatcher.sendFileDescriptor(None, "")
+        
+        self.assertEqual(sockets[0].status, 1)
+        self.assertEqual(sockets[1].status, 1)
+        self.assertEqual(sockets[2].status, None)

Modified: CalendarServer/trunk/twext/web2/metafd.py
===================================================================
--- CalendarServer/trunk/twext/web2/metafd.py	2012-05-15 15:38:47 UTC (rev 9248)
+++ CalendarServer/trunk/twext/web2/metafd.py	2012-05-16 14:35:02 UTC (rev 9249)
@@ -15,20 +15,19 @@
 # limitations under the License.
 ##
 
-from twisted.internet.tcp import Server
+from twext.internet.sendfdport import (
+    InheritedPort, InheritedSocketDispatcher, InheritingProtocolFactory)
 from twext.internet.tcp import MaxAcceptTCPServer
-
+from twext.python.log import Logger
+from twext.web2.channel.http import HTTPFactory
+from twisted.application.service import MultiService, Service
 from twisted.internet import reactor
+from twisted.internet.tcp import Server
 
-from twisted.application.service import MultiService, Service
+log = Logger()
 
-from twext.web2.channel.http import HTTPFactory
 
-from twext.internet.sendfdport import (
-    InheritedPort, InheritedSocketDispatcher, InheritingProtocolFactory)
 
-
-
 class JustEnoughLikeAPort(object):
     """
     Fake out just enough of L{tcp.Port} to be acceptable to
@@ -198,10 +197,19 @@
                 # accepting connections again if we paused (see
                 # newConnectionStatus)
                 result = self.intWithNoneAsZero(previousStatus) - 1
+                if result < 0:
+                    log.err("metafd: trying to decrement status below zero")
+                    result = 0
             else:
                 # A new process just started accepting new connections; zero
-                # out its expected load.
-                result = 0
+                # out its expected load, but only if previous status is still None
+                result = 0 if previousStatus is None else previousStatus
+                if previousStatus is None:
+                    result = 0
+                else:
+                    log.err("metafd: trying to zero status that is not None")
+                    result = previousStatus
+
             # If load has indeed decreased (i.e. in any case except 'a new,
             # idle process replaced an old, idle process'), then start
             # listening again.

Modified: CalendarServer/trunk/twext/web2/test/test_metafd.py
===================================================================
--- CalendarServer/trunk/twext/web2/test/test_metafd.py	2012-05-15 15:38:47 UTC (rev 9248)
+++ CalendarServer/trunk/twext/web2/test/test_metafd.py	2012-05-16 14:35:02 UTC (rev 9249)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2011 Apple Inc. All rights reserved.
+# Copyright (c) 2011-2012 Apple Inc. All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -22,13 +22,11 @@
 from socket import error as SocketError, AF_INET
 from errno import ENOTCONN
 
+from twext.internet import sendfdport
 from twext.web2 import metafd
-from twext.web2.metafd import ReportingHTTPService
 from twext.web2.channel.http import HTTPChannel
-from twext.internet import sendfdport
+from twext.web2.metafd import ReportingHTTPService, ConnectionLimiter
 from twisted.internet.tcp import Server
-
-
 from twisted.trial.unittest import TestCase
 
 
@@ -142,3 +140,37 @@
         self.assertEqual(len(channels), 1)
         self.assertEqual(list(channels)[0].transport.getPeer().host, "0.0.0.0")
 
+
+
+class ConnectionLimiterTests(TestCase):
+    """
+    Tests for L{ConnectionLimiter}
+    """
+    
+    
+    def test_statusFromMessage(self):
+        """
+        Test ConnectionLimiter.statusFromMessage to make sure count cannot go below zero and that
+        zeroing out does not wipe out an existing count.
+        """
+        
+        cl = ConnectionLimiter(2, 20)
+        
+        # "0" Zeroing out does not overwrite legitimate count
+        self.assertEqual(cl.statusFromMessage(None, "0"), 0)
+        self.assertEqual(cl.statusFromMessage(0, "0"), 0)
+        self.assertEqual(cl.statusFromMessage(1, "0"), 1)
+        self.assertEqual(cl.statusFromMessage(2, "0"), 2)
+        
+        # "-" No negative counts
+        self.assertEqual(cl.statusFromMessage(None, "-"), 0)
+        self.assertEqual(cl.statusFromMessage(0, "-"), 0)
+        self.assertEqual(cl.statusFromMessage(1, "-"), 0)
+        self.assertEqual(cl.statusFromMessage(2, "-"), 1)
+        
+        # "+" No change
+        self.assertEqual(cl.statusFromMessage(None, "+"), 0)
+        self.assertEqual(cl.statusFromMessage(0, "+"), 0)
+        self.assertEqual(cl.statusFromMessage(1, "+"), 1)
+        self.assertEqual(cl.statusFromMessage(2, "+"), 2)
+
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120516/13c8b2a4/attachment.html>


More information about the calendarserver-changes mailing list