310 lines
10 KiB
Python
310 lines
10 KiB
Python
#!/usr/bin/python
|
|
# @file test_mysql_pool.py
|
|
# @brief Test cases for mysql_pool
|
|
#
|
|
# Copyright (c) 2007, Linden Research, Inc.
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in
|
|
# all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
# THE SOFTWARE.
|
|
|
|
import os.path
|
|
|
|
from eventlet import api, coros, tests
|
|
from eventlet import db_pool
|
|
|
|
class DBTester(object):
|
|
def setUp(self):
|
|
self.create_db()
|
|
self.connection = None
|
|
connection = self._dbmodule.connect(**self._auth)
|
|
cursor = connection.cursor()
|
|
cursor.execute("""CREATE TABLE gargleblatz
|
|
(
|
|
a INTEGER
|
|
) ENGINE = InnoDB;""")
|
|
connection.commit()
|
|
cursor.close()
|
|
|
|
def tearDown(self):
|
|
if self.connection is not None:
|
|
self.connection.close()
|
|
self.drop_db()
|
|
|
|
def set_up_test_table(self, connection = None):
|
|
if connection is None:
|
|
if self.connection is None:
|
|
self.connection = self._dbmodule.connect(**self._auth)
|
|
connection = self.connection
|
|
|
|
cursor = connection.cursor()
|
|
cursor.execute("""CREATE TEMPORARY TABLE test_table
|
|
(
|
|
row_id INTEGER PRIMARY KEY AUTO_INCREMENT,
|
|
value_int INTEGER,
|
|
value_float FLOAT,
|
|
value_string VARCHAR(200),
|
|
value_uuid CHAR(36),
|
|
value_binary BLOB,
|
|
value_binary_string VARCHAR(200) BINARY,
|
|
value_enum ENUM('Y','N'),
|
|
created TIMESTAMP
|
|
) ENGINE = InnoDB;""")
|
|
connection.commit()
|
|
cursor.close()
|
|
|
|
class TestDBConnectionPool(DBTester):
|
|
def setUp(self):
|
|
super(TestDBConnectionPool, self).setUp()
|
|
self.pool = self.create_pool()
|
|
self.connection = self.pool.get()
|
|
|
|
def tearDown(self):
|
|
self.pool.put(self.connection)
|
|
super(TestDBConnectionPool, self).tearDown()
|
|
|
|
def assert_cursor_works(self, cursor):
|
|
cursor.execute("show full processlist")
|
|
rows = cursor.fetchall()
|
|
self.assert_(rows)
|
|
|
|
def test_connecting(self):
|
|
self.assert_(self.connection is not None)
|
|
|
|
def test_create_cursor(self):
|
|
cursor = self.connection.cursor()
|
|
cursor.close()
|
|
|
|
def test_run_query(self):
|
|
cursor = self.connection.cursor()
|
|
self.assert_cursor_works(cursor)
|
|
cursor.close()
|
|
|
|
def test_run_bad_query(self):
|
|
cursor = self.connection.cursor()
|
|
try:
|
|
cursor.execute("garbage blah blah")
|
|
self.assert_(False)
|
|
except AssertionError:
|
|
raise
|
|
except Exception, e:
|
|
pass
|
|
cursor.close()
|
|
|
|
def test_put_none(self):
|
|
# the pool is of size 1, and its only connection is out
|
|
self.assert_(self.pool.free() == 0)
|
|
self.pool.put(None)
|
|
# ha ha we fooled it into thinking that we had a dead process
|
|
self.assert_(self.pool.free() == 1)
|
|
conn2 = self.pool.get()
|
|
self.assert_(conn2 is not None)
|
|
self.assert_(conn2.cursor)
|
|
del conn2
|
|
|
|
def test_close_does_a_put(self):
|
|
self.assert_(self.pool.free() == 0)
|
|
self.connection.close()
|
|
self.assert_(self.pool.free() == 1)
|
|
self.assertRaises(AttributeError, self.connection.cursor)
|
|
|
|
def test_deletion_does_a_put(self):
|
|
self.assert_(self.pool.free() == 0)
|
|
self.connection = None
|
|
self.assert_(self.pool.free() == 1)
|
|
|
|
def test_put_doesnt_double_wrap(self):
|
|
self.pool.put(self.connection)
|
|
conn = self.pool.get()
|
|
self.assert_(not isinstance(conn._base, db_pool.PooledConnectionWrapper))
|
|
|
|
def test_bool(self):
|
|
self.assert_(self.connection)
|
|
self.connection.close()
|
|
self.assert_(not self.connection)
|
|
|
|
def fill_test_table(self, conn):
|
|
curs = conn.cursor()
|
|
for i in range(1000):
|
|
curs.execute('insert into test_table (value_int) values (%s)' % i)
|
|
conn.commit()
|
|
|
|
def test_returns_immediately(self):
|
|
self.pool = self.create_pool()
|
|
conn = self.pool.get()
|
|
self.set_up_test_table(conn)
|
|
self.fill_test_table(conn)
|
|
curs = conn.cursor()
|
|
results = []
|
|
SHORT_QUERY = "select * from test_table"
|
|
evt = coros.event()
|
|
def a_query():
|
|
self.assert_cursor_works(curs)
|
|
curs.execute(SHORT_QUERY)
|
|
results.append(2)
|
|
evt.send()
|
|
evt2 = coros.event()
|
|
api.spawn(a_query)
|
|
results.append(1)
|
|
self.assertEqual([1], results)
|
|
evt.wait()
|
|
self.assertEqual([1, 2], results)
|
|
|
|
def test_connection_is_clean_after_put(self):
|
|
self.pool = self.create_pool()
|
|
conn = self.pool.get()
|
|
self.set_up_test_table(conn)
|
|
curs = conn.cursor()
|
|
for i in range(10):
|
|
curs.execute('insert into test_table (value_int) values (%s)' % i)
|
|
# do not commit :-)
|
|
self.pool.put(conn)
|
|
del conn
|
|
conn2 = self.pool.get()
|
|
curs2 = conn2.cursor()
|
|
for i in range(10):
|
|
curs2.execute('insert into test_table (value_int) values (%s)' % i)
|
|
conn2.commit()
|
|
rows = curs2.execute("select * from test_table")
|
|
# we should have only inserted them once
|
|
self.assertEqual(10, rows)
|
|
|
|
def test_visibility_from_other_connections(self):
|
|
# *FIX: use some non-indra-specific table for testing (can't use a temp table)
|
|
self.pool = self.create_pool(3)
|
|
conn = self.pool.get()
|
|
conn2 = self.pool.get()
|
|
curs = conn.cursor()
|
|
try:
|
|
curs2 = conn2.cursor()
|
|
rows2 = curs2.execute("insert into gargleblatz (a) values (%s)" % (314159))
|
|
self.assertEqual(rows2, 1)
|
|
conn2.commit()
|
|
selection_query = "select * from gargleblatz"
|
|
rows2 = curs2.execute(selection_query)
|
|
self.assertEqual(rows2, 1)
|
|
del curs2
|
|
del conn2
|
|
# create a new connection, it should see the addition
|
|
conn3 = self.pool.get()
|
|
curs3 = conn3.cursor()
|
|
rows3 = curs3.execute(selection_query)
|
|
self.assertEqual(rows3, 1)
|
|
# now, does the already-open connection see it?
|
|
rows = curs.execute(selection_query)
|
|
self.assertEqual(rows, 1)
|
|
finally:
|
|
# clean up my litter
|
|
curs.execute("delete from gargleblatz where a=314159")
|
|
conn.commit()
|
|
|
|
|
|
def test_two_simultaneous_connections(self):
|
|
self.pool = self.create_pool(2)
|
|
conn = self.pool.get()
|
|
self.set_up_test_table(conn)
|
|
self.fill_test_table(conn)
|
|
curs = conn.cursor()
|
|
conn2 = self.pool.get()
|
|
self.set_up_test_table(conn2)
|
|
self.fill_test_table(conn2)
|
|
curs2 = conn2.cursor()
|
|
results = []
|
|
LONG_QUERY = "select * from test_table"
|
|
SHORT_QUERY = "select * from test_table where row_id <= 20"
|
|
|
|
evt = coros.event()
|
|
def long_running_query():
|
|
self.assert_cursor_works(curs)
|
|
curs.execute(LONG_QUERY)
|
|
results.append(1)
|
|
evt.send()
|
|
evt2 = coros.event()
|
|
def short_running_query():
|
|
self.assert_cursor_works(curs2)
|
|
curs2.execute(SHORT_QUERY)
|
|
results.append(2)
|
|
evt2.send()
|
|
|
|
api.spawn(long_running_query)
|
|
api.spawn(short_running_query)
|
|
evt.wait()
|
|
evt2.wait()
|
|
#print "results %s" % results
|
|
results.sort()
|
|
self.assertEqual([1, 2], results)
|
|
|
|
|
|
class TestTpoolConnectionPool(TestDBConnectionPool):
|
|
def create_pool(self, max_items = 1):
|
|
return db_pool.TpooledConnectionPool(self._dbmodule, 0, max_items, **self._auth)
|
|
|
|
|
|
class TestSaranwrapConnectionPool(TestDBConnectionPool):
|
|
def create_pool(self, max_items = 1):
|
|
return db_pool.SaranwrappedConnectionPool(self._dbmodule, 0, max_items, **self._auth)
|
|
|
|
class TestMysqlConnectionPool(object):
|
|
def setUp(self):
|
|
import MySQLdb
|
|
self._dbmodule = MySQLdb
|
|
try:
|
|
import simplejson
|
|
import os.path
|
|
auth_utf8 = simplejson.load(open(os.path.join(os.path.dirname(__file__), 'auth.json')))
|
|
# have to convert unicode objects to str objects because mysqldb is dum
|
|
self._auth = dict([(str(k), str(v))
|
|
for k, v in auth_utf8.items()])
|
|
except (IOError, ImportError), e:
|
|
self._auth = {'host': 'localhost','user': 'root','passwd': '','db': 'persist0'}
|
|
super(TestMysqlConnectionPool, self).setUp()
|
|
|
|
def create_db(self):
|
|
auth = self._auth.copy()
|
|
try:
|
|
self.drop_db()
|
|
except Exception:
|
|
pass
|
|
dbname = auth.pop('db')
|
|
db = self._dbmodule.connect(**auth).cursor()
|
|
db.execute("create database "+dbname)
|
|
db.close()
|
|
del db
|
|
|
|
def drop_db(self):
|
|
db = self._dbmodule.connect(**self._auth).cursor()
|
|
db.execute("drop database "+self._auth['db'])
|
|
db.close()
|
|
del db
|
|
|
|
class TestMysqlTpool(TestMysqlConnectionPool, TestTpoolConnectionPool, tests.TestCase):
|
|
pass
|
|
|
|
class TestMysqlSaranwrap(TestMysqlConnectionPool, TestSaranwrapConnectionPool, tests.TestCase):
|
|
pass
|
|
|
|
|
|
if __name__ == '__main__':
|
|
try:
|
|
import MySQLdb
|
|
except ImportError:
|
|
print "Unable to import MySQLdb, skipping db_pool_test."
|
|
else:
|
|
tests.main()
|
|
else:
|
|
import MySQLdb
|