/Lib/bsddb/test/test_thread.py

http://unladen-swallow.googlecode.com/ · Python · 528 lines · 426 code · 94 blank · 8 comment · 95 complexity · 1f94b76f7c83d723a7a9929ad1b9c422 MD5 · raw file

  1. """TestCases for multi-threaded access to a DB.
  2. """
  3. import os
  4. import sys
  5. import time
  6. import errno
  7. from random import random
  8. DASH = '-'
  9. try:
  10. WindowsError
  11. except NameError:
  12. class WindowsError(Exception):
  13. pass
  14. import unittest
  15. from test_all import db, dbutils, test_support, verbose, have_threads, \
  16. get_new_environment_path, get_new_database_path
  17. if have_threads :
  18. from threading import Thread
  19. import sys
  20. if sys.version_info[0] < 3 :
  21. from threading import currentThread
  22. else :
  23. from threading import current_thread as currentThread
  24. #----------------------------------------------------------------------
  25. class BaseThreadedTestCase(unittest.TestCase):
  26. dbtype = db.DB_UNKNOWN # must be set in derived class
  27. dbopenflags = 0
  28. dbsetflags = 0
  29. envflags = 0
  30. import sys
  31. if sys.version_info[:3] < (2, 4, 0):
  32. def assertTrue(self, expr, msg=None):
  33. self.failUnless(expr,msg=msg)
  34. def setUp(self):
  35. if verbose:
  36. dbutils._deadlock_VerboseFile = sys.stdout
  37. self.homeDir = get_new_environment_path()
  38. self.env = db.DBEnv()
  39. self.setEnvOpts()
  40. self.env.open(self.homeDir, self.envflags | db.DB_CREATE)
  41. self.filename = self.__class__.__name__ + '.db'
  42. self.d = db.DB(self.env)
  43. if self.dbsetflags:
  44. self.d.set_flags(self.dbsetflags)
  45. self.d.open(self.filename, self.dbtype, self.dbopenflags|db.DB_CREATE)
  46. def tearDown(self):
  47. self.d.close()
  48. self.env.close()
  49. test_support.rmtree(self.homeDir)
  50. def setEnvOpts(self):
  51. pass
  52. def makeData(self, key):
  53. return DASH.join([key] * 5)
  54. #----------------------------------------------------------------------
  55. class ConcurrentDataStoreBase(BaseThreadedTestCase):
  56. dbopenflags = db.DB_THREAD
  57. envflags = db.DB_THREAD | db.DB_INIT_CDB | db.DB_INIT_MPOOL
  58. readers = 0 # derived class should set
  59. writers = 0
  60. records = 1000
  61. def test01_1WriterMultiReaders(self):
  62. if verbose:
  63. print '\n', '-=' * 30
  64. print "Running %s.test01_1WriterMultiReaders..." % \
  65. self.__class__.__name__
  66. keys=range(self.records)
  67. import random
  68. random.shuffle(keys)
  69. records_per_writer=self.records//self.writers
  70. readers_per_writer=self.readers//self.writers
  71. self.assertEqual(self.records,self.writers*records_per_writer)
  72. self.assertEqual(self.readers,self.writers*readers_per_writer)
  73. self.assertTrue((records_per_writer%readers_per_writer)==0)
  74. readers = []
  75. for x in xrange(self.readers):
  76. rt = Thread(target = self.readerThread,
  77. args = (self.d, x),
  78. name = 'reader %d' % x,
  79. )#verbose = verbose)
  80. import sys
  81. if sys.version_info[0] < 3 :
  82. rt.setDaemon(True)
  83. else :
  84. rt.daemon = True
  85. readers.append(rt)
  86. writers=[]
  87. for x in xrange(self.writers):
  88. a=keys[records_per_writer*x:records_per_writer*(x+1)]
  89. a.sort() # Generate conflicts
  90. b=readers[readers_per_writer*x:readers_per_writer*(x+1)]
  91. wt = Thread(target = self.writerThread,
  92. args = (self.d, a, b),
  93. name = 'writer %d' % x,
  94. )#verbose = verbose)
  95. writers.append(wt)
  96. for t in writers:
  97. import sys
  98. if sys.version_info[0] < 3 :
  99. t.setDaemon(True)
  100. else :
  101. t.daemon = True
  102. t.start()
  103. for t in writers:
  104. t.join()
  105. for t in readers:
  106. t.join()
  107. def writerThread(self, d, keys, readers):
  108. import sys
  109. if sys.version_info[0] < 3 :
  110. name = currentThread().getName()
  111. else :
  112. name = currentThread().name
  113. if verbose:
  114. print "%s: creating records %d - %d" % (name, start, stop)
  115. count=len(keys)//len(readers)
  116. count2=count
  117. for x in keys :
  118. key = '%04d' % x
  119. dbutils.DeadlockWrap(d.put, key, self.makeData(key),
  120. max_retries=12)
  121. if verbose and x % 100 == 0:
  122. print "%s: records %d - %d finished" % (name, start, x)
  123. count2-=1
  124. if not count2 :
  125. readers.pop().start()
  126. count2=count
  127. if verbose:
  128. print "%s: finished creating records" % name
  129. if verbose:
  130. print "%s: thread finished" % name
  131. def readerThread(self, d, readerNum):
  132. import sys
  133. if sys.version_info[0] < 3 :
  134. name = currentThread().getName()
  135. else :
  136. name = currentThread().name
  137. for i in xrange(5) :
  138. c = d.cursor()
  139. count = 0
  140. rec = c.first()
  141. while rec:
  142. count += 1
  143. key, data = rec
  144. self.assertEqual(self.makeData(key), data)
  145. rec = c.next()
  146. if verbose:
  147. print "%s: found %d records" % (name, count)
  148. c.close()
  149. if verbose:
  150. print "%s: thread finished" % name
  151. class BTreeConcurrentDataStore(ConcurrentDataStoreBase):
  152. dbtype = db.DB_BTREE
  153. writers = 2
  154. readers = 10
  155. records = 1000
  156. class HashConcurrentDataStore(ConcurrentDataStoreBase):
  157. dbtype = db.DB_HASH
  158. writers = 2
  159. readers = 10
  160. records = 1000
  161. #----------------------------------------------------------------------
  162. class SimpleThreadedBase(BaseThreadedTestCase):
  163. dbopenflags = db.DB_THREAD
  164. envflags = db.DB_THREAD | db.DB_INIT_MPOOL | db.DB_INIT_LOCK
  165. readers = 10
  166. writers = 2
  167. records = 1000
  168. def setEnvOpts(self):
  169. self.env.set_lk_detect(db.DB_LOCK_DEFAULT)
  170. def test02_SimpleLocks(self):
  171. if verbose:
  172. print '\n', '-=' * 30
  173. print "Running %s.test02_SimpleLocks..." % self.__class__.__name__
  174. keys=range(self.records)
  175. import random
  176. random.shuffle(keys)
  177. records_per_writer=self.records//self.writers
  178. readers_per_writer=self.readers//self.writers
  179. self.assertEqual(self.records,self.writers*records_per_writer)
  180. self.assertEqual(self.readers,self.writers*readers_per_writer)
  181. self.assertTrue((records_per_writer%readers_per_writer)==0)
  182. readers = []
  183. for x in xrange(self.readers):
  184. rt = Thread(target = self.readerThread,
  185. args = (self.d, x),
  186. name = 'reader %d' % x,
  187. )#verbose = verbose)
  188. import sys
  189. if sys.version_info[0] < 3 :
  190. rt.setDaemon(True)
  191. else :
  192. rt.daemon = True
  193. readers.append(rt)
  194. writers = []
  195. for x in xrange(self.writers):
  196. a=keys[records_per_writer*x:records_per_writer*(x+1)]
  197. a.sort() # Generate conflicts
  198. b=readers[readers_per_writer*x:readers_per_writer*(x+1)]
  199. wt = Thread(target = self.writerThread,
  200. args = (self.d, a, b),
  201. name = 'writer %d' % x,
  202. )#verbose = verbose)
  203. writers.append(wt)
  204. for t in writers:
  205. import sys
  206. if sys.version_info[0] < 3 :
  207. t.setDaemon(True)
  208. else :
  209. t.daemon = True
  210. t.start()
  211. for t in writers:
  212. t.join()
  213. for t in readers:
  214. t.join()
  215. def writerThread(self, d, keys, readers):
  216. import sys
  217. if sys.version_info[0] < 3 :
  218. name = currentThread().getName()
  219. else :
  220. name = currentThread().name
  221. if verbose:
  222. print "%s: creating records %d - %d" % (name, start, stop)
  223. count=len(keys)//len(readers)
  224. count2=count
  225. for x in keys :
  226. key = '%04d' % x
  227. dbutils.DeadlockWrap(d.put, key, self.makeData(key),
  228. max_retries=12)
  229. if verbose and x % 100 == 0:
  230. print "%s: records %d - %d finished" % (name, start, x)
  231. count2-=1
  232. if not count2 :
  233. readers.pop().start()
  234. count2=count
  235. if verbose:
  236. print "%s: thread finished" % name
  237. def readerThread(self, d, readerNum):
  238. import sys
  239. if sys.version_info[0] < 3 :
  240. name = currentThread().getName()
  241. else :
  242. name = currentThread().name
  243. c = d.cursor()
  244. count = 0
  245. rec = dbutils.DeadlockWrap(c.first, max_retries=10)
  246. while rec:
  247. count += 1
  248. key, data = rec
  249. self.assertEqual(self.makeData(key), data)
  250. rec = dbutils.DeadlockWrap(c.next, max_retries=10)
  251. if verbose:
  252. print "%s: found %d records" % (name, count)
  253. c.close()
  254. if verbose:
  255. print "%s: thread finished" % name
  256. class BTreeSimpleThreaded(SimpleThreadedBase):
  257. dbtype = db.DB_BTREE
  258. class HashSimpleThreaded(SimpleThreadedBase):
  259. dbtype = db.DB_HASH
  260. #----------------------------------------------------------------------
  261. class ThreadedTransactionsBase(BaseThreadedTestCase):
  262. dbopenflags = db.DB_THREAD | db.DB_AUTO_COMMIT
  263. envflags = (db.DB_THREAD |
  264. db.DB_INIT_MPOOL |
  265. db.DB_INIT_LOCK |
  266. db.DB_INIT_LOG |
  267. db.DB_INIT_TXN
  268. )
  269. readers = 0
  270. writers = 0
  271. records = 2000
  272. txnFlag = 0
  273. def setEnvOpts(self):
  274. #self.env.set_lk_detect(db.DB_LOCK_DEFAULT)
  275. pass
  276. def test03_ThreadedTransactions(self):
  277. if verbose:
  278. print '\n', '-=' * 30
  279. print "Running %s.test03_ThreadedTransactions..." % \
  280. self.__class__.__name__
  281. keys=range(self.records)
  282. import random
  283. random.shuffle(keys)
  284. records_per_writer=self.records//self.writers
  285. readers_per_writer=self.readers//self.writers
  286. self.assertEqual(self.records,self.writers*records_per_writer)
  287. self.assertEqual(self.readers,self.writers*readers_per_writer)
  288. self.assertTrue((records_per_writer%readers_per_writer)==0)
  289. readers=[]
  290. for x in xrange(self.readers):
  291. rt = Thread(target = self.readerThread,
  292. args = (self.d, x),
  293. name = 'reader %d' % x,
  294. )#verbose = verbose)
  295. import sys
  296. if sys.version_info[0] < 3 :
  297. rt.setDaemon(True)
  298. else :
  299. rt.daemon = True
  300. readers.append(rt)
  301. writers = []
  302. for x in xrange(self.writers):
  303. a=keys[records_per_writer*x:records_per_writer*(x+1)]
  304. b=readers[readers_per_writer*x:readers_per_writer*(x+1)]
  305. wt = Thread(target = self.writerThread,
  306. args = (self.d, a, b),
  307. name = 'writer %d' % x,
  308. )#verbose = verbose)
  309. writers.append(wt)
  310. dt = Thread(target = self.deadlockThread)
  311. import sys
  312. if sys.version_info[0] < 3 :
  313. dt.setDaemon(True)
  314. else :
  315. dt.daemon = True
  316. dt.start()
  317. for t in writers:
  318. import sys
  319. if sys.version_info[0] < 3 :
  320. t.setDaemon(True)
  321. else :
  322. t.daemon = True
  323. t.start()
  324. for t in writers:
  325. t.join()
  326. for t in readers:
  327. t.join()
  328. self.doLockDetect = False
  329. dt.join()
  330. def writerThread(self, d, keys, readers):
  331. import sys
  332. if sys.version_info[0] < 3 :
  333. name = currentThread().getName()
  334. else :
  335. name = currentThread().name
  336. count=len(keys)//len(readers)
  337. while len(keys):
  338. try:
  339. txn = self.env.txn_begin(None, self.txnFlag)
  340. keys2=keys[:count]
  341. for x in keys2 :
  342. key = '%04d' % x
  343. d.put(key, self.makeData(key), txn)
  344. if verbose and x % 100 == 0:
  345. print "%s: records %d - %d finished" % (name, start, x)
  346. txn.commit()
  347. keys=keys[count:]
  348. readers.pop().start()
  349. except (db.DBLockDeadlockError, db.DBLockNotGrantedError), val:
  350. if verbose:
  351. print "%s: Aborting transaction (%s)" % (name, val[1])
  352. txn.abort()
  353. if verbose:
  354. print "%s: thread finished" % name
  355. def readerThread(self, d, readerNum):
  356. import sys
  357. if sys.version_info[0] < 3 :
  358. name = currentThread().getName()
  359. else :
  360. name = currentThread().name
  361. finished = False
  362. while not finished:
  363. try:
  364. txn = self.env.txn_begin(None, self.txnFlag)
  365. c = d.cursor(txn)
  366. count = 0
  367. rec = c.first()
  368. while rec:
  369. count += 1
  370. key, data = rec
  371. self.assertEqual(self.makeData(key), data)
  372. rec = c.next()
  373. if verbose: print "%s: found %d records" % (name, count)
  374. c.close()
  375. txn.commit()
  376. finished = True
  377. except (db.DBLockDeadlockError, db.DBLockNotGrantedError), val:
  378. if verbose:
  379. print "%s: Aborting transaction (%s)" % (name, val[1])
  380. c.close()
  381. txn.abort()
  382. if verbose:
  383. print "%s: thread finished" % name
  384. def deadlockThread(self):
  385. self.doLockDetect = True
  386. while self.doLockDetect:
  387. time.sleep(0.05)
  388. try:
  389. aborted = self.env.lock_detect(
  390. db.DB_LOCK_RANDOM, db.DB_LOCK_CONFLICT)
  391. if verbose and aborted:
  392. print "deadlock: Aborted %d deadlocked transaction(s)" \
  393. % aborted
  394. except db.DBError:
  395. pass
  396. class BTreeThreadedTransactions(ThreadedTransactionsBase):
  397. dbtype = db.DB_BTREE
  398. writers = 2
  399. readers = 10
  400. records = 1000
  401. class HashThreadedTransactions(ThreadedTransactionsBase):
  402. dbtype = db.DB_HASH
  403. writers = 2
  404. readers = 10
  405. records = 1000
  406. class BTreeThreadedNoWaitTransactions(ThreadedTransactionsBase):
  407. dbtype = db.DB_BTREE
  408. writers = 2
  409. readers = 10
  410. records = 1000
  411. txnFlag = db.DB_TXN_NOWAIT
  412. class HashThreadedNoWaitTransactions(ThreadedTransactionsBase):
  413. dbtype = db.DB_HASH
  414. writers = 2
  415. readers = 10
  416. records = 1000
  417. txnFlag = db.DB_TXN_NOWAIT
  418. #----------------------------------------------------------------------
  419. def test_suite():
  420. suite = unittest.TestSuite()
  421. if have_threads:
  422. suite.addTest(unittest.makeSuite(BTreeConcurrentDataStore))
  423. suite.addTest(unittest.makeSuite(HashConcurrentDataStore))
  424. suite.addTest(unittest.makeSuite(BTreeSimpleThreaded))
  425. suite.addTest(unittest.makeSuite(HashSimpleThreaded))
  426. suite.addTest(unittest.makeSuite(BTreeThreadedTransactions))
  427. suite.addTest(unittest.makeSuite(HashThreadedTransactions))
  428. suite.addTest(unittest.makeSuite(BTreeThreadedNoWaitTransactions))
  429. suite.addTest(unittest.makeSuite(HashThreadedNoWaitTransactions))
  430. else:
  431. print "Threads not available, skipping thread tests."
  432. return suite
  433. if __name__ == '__main__':
  434. unittest.main(defaultTest='test_suite')