constrictor SIP2; login repair; item info load tests
authorBill Erickson <berick@esilibrary.com>
Tue, 7 May 2013 20:30:52 +0000 (16:30 -0400)
committerBill Erickson <berick@esilibrary.com>
Tue, 7 May 2013 20:32:47 +0000 (16:32 -0400)
* login occurs within a thread lock to prevent multiple in-flight
logins (for a single username), which Evergreen does not handle well.

* add # iterations and task delay settings for load testing scripts

Signed-off-by: Bill Erickson <berick@esilibrary.com>
constrictor.properties
contrib/sip2/sip2_client.py
contrib/sip2/sip2_item_info_endurance.py
contrib/sip2/sip2_mixed_batch.py
contrib/sip2/sip2_patron_info_endurance.py
contrib/sip2/sip2_patron_status_endurance.py

index 54b24cc..5cebd78 100644 (file)
@@ -97,4 +97,10 @@ sip2.copyBarcodes=
 sip2.userBarcodes=
 # passwords must correspond to user barcodes by position in the list
 sip2.userPasswords=
+# ms delay between requests;  used for load testing
+sip2.load.delay=100
+# number of requests to make per thread
+# this is used in lieu of iterations since each iteration requires 
+# a SIP login, which tests a different kind of load entirely
+sip2.load.iterations=100
 
index dd6ce9b..4f41be3 100644 (file)
@@ -103,44 +103,48 @@ class SIP2Client(object):
 
         return data
 
-    def login(self, username, password, institution):
+    def login(self, script, username, password, institution):
         client = self
 
         class LoginTask(constrictor.task.Task):
+            ''' login occurs within a thread lock to allow for 
+            complete login before new logins are attempted '''
 
             def __init__(self):
                 constrictor.task.Task.__init__(self, self.__class__.__name__)
 
             def run(self, **kw):
 
+                resp = None
+                script.lock.acquire()
+                try:
+                    resp = self.send_login()
+                finally:
+                    script.lock.release()
+
+                return resp
+
+            def send_login(self):
+
                 log.log_info("LoginTask: %s %s %s" % (username, password, institution))
 
                 msg = '9300CN%s|CO%s|CP%s|' % (username, password, institution)
 
-                for i in range(3):
-                    
-                    client.send_msg(msg)
-                    data = client.recv_msg()
+                client.send_msg(msg)
+                data = client.recv_msg()
 
-                    if data is None:
-                        # let's start over and try again
-                        time.sleep(random.random() / 100)
-                        client.disconnect()
-                        client.init_socket()
-                        continue
+                if data is None:
+                    log.log_error("SIP2 login failed with no server response")
+                    return False
 
-                    if data[:3] == '941':
-                        log.log_info("SIP2 login OK")
-                        return True
+                elif data[:3] == '941':
+                    log.log_info("SIP2 login OK")
+                    return True
 
-                    else:
-                        log.log_error("SIP2 login failed: %s" % data)
-                        return False
-
-                log.log_error("SIP2 login failed with no server response")
-                return False
+                else:
+                    log.log_error("SIP2 login failed: %s" % data)
+                    return False
 
-        time.sleep(random.random() / 100) # reduce the chance of initial login collisions
         return LoginTask().start()
 
     def item_info_request(self, institution, copy_barcode):
index 3774fba..36c6c26 100644 (file)
@@ -13,6 +13,7 @@
 # GNU General Public License for more details.
 # -----------------------------------------------------------------------
 
+import time
 import constrictor.script
 import constrictor.properties
 import constrictor.log as log
@@ -32,17 +33,44 @@ class SIP2ItemInfoEnduranceScript(constrictor.script.Script):
         institution = props.get_property('sip2.institution')
         server = props.get_property('sip2.server')
         port = int(props.get_property('sip2.port'))
-
         barcodes = props.get_property('sip2.copyBarcodes').split(',')
-        copy_barcode = barcodes[constrictor.script.ScriptThread.get_thread_id()]
+        iters = int(props.get_property('sip2.load.iterations')) or 10
+        delay = props.get_property('sip2.load.delay') or 0
+        delay = float(delay) / 1000 # milliseconds
+
+        log.log_info(
+            "SIP2ItemInfoEndurance iterations=%d delay=%f" % (
+                iters, delay))
 
         client = sip2_client.SIP2Client(server, port)
-        client.init_socket()
+        if not client.init_socket():
+            log.log_error("Unable to talk to SIP server at %s" % server)
+            return
+
+        if not client.login(self, username, password, institution):
+            log.log_error("SIP login failed user=%s pass=%s institution=%s" % (
+                username, password, institution))
+            return
+
+        # with this simple loop, threads are sharing barcodes.
+        # since these are info requests only (i.e. no data is
+        # affected), that should be OK.
+        barcode_idx = 0;
+        barcode_cnt = len(barcodes)
+        for i in range(iters):
+
+            if barcode_idx == barcode_cnt: barcode_idx = 0
+            barcode = barcodes[barcode_idx]
+            barcode_idx += 1
+
+            log.log_debug("SIP item_info_request for %s" % barcode)
+
+            if not client.item_info_request(institution, barcode):
+                log.log_error(
+                    "Error calling item_info_request for '%s'" % barcode)
+                break
 
-        if client.login(username, password, institution):
-            for i in range(100):
-                if not client.item_info_request(institution, copy_barcode):
-                    break
+            if delay: time.sleep(delay)
 
         client.disconnect()
 
index 0b1b9bd..512e7c0 100644 (file)
@@ -46,7 +46,7 @@ class SIP2MixedBatchScript(constrictor.script.Script):
         client = sip2_client.SIP2Client(server, port)
         client.init_socket()
 
-        if client.login(username, password, institution):
+        if client.login(self, username, password, institution):
             for i in range(100):
                 if not client.item_info_request(institution, copy_barcode):
                     break
index 90c1059..e9c74d1 100644 (file)
@@ -38,7 +38,7 @@ class SIP2PatronInfoEnduranceScript(constrictor.script.Script):
 
         client = sip2_client.SIP2Client(server, port)
 
-        if client.init_socket() and client.login(username, password, institution):
+        if client.init_socket() and client.login(self, username, password, institution):
             for i in range(100):
                 if not client.patron_info_request(institution, user_barcode):
                     break
index 1ecac40..61899ad 100644 (file)
@@ -41,7 +41,7 @@ class SIP2PatronStatusEnduranceScript(constrictor.script.Script):
 
         client = sip2_client.SIP2Client(server, port)
 
-        if client.init_socket() and client.login(username, password, institution):
+        if client.init_socket() and client.login(self, username, password, institution):
             for i in range(100):
                 if not client.patron_status_request(institution, user_barcode, user_password):
                     break