/Lib/bsddb/test/test_replication.py

http://unladen-swallow.googlecode.com/ · Python · 456 lines · 350 code · 75 blank · 31 comment · 69 complexity · e5f6d0aafa2fafc1ba24c469b4f1134a MD5 · raw file

  1. """TestCases for distributed transactions.
  2. """
  3. import os
  4. import time
  5. import unittest
  6. from test_all import db, test_support, have_threads, verbose, \
  7. get_new_environment_path, get_new_database_path
  8. #----------------------------------------------------------------------
  9. class DBReplicationManager(unittest.TestCase):
  10. import sys
  11. if sys.version_info[:3] < (2, 4, 0):
  12. def assertTrue(self, expr, msg=None):
  13. self.failUnless(expr,msg=msg)
  14. def setUp(self) :
  15. self.homeDirMaster = get_new_environment_path()
  16. self.homeDirClient = get_new_environment_path()
  17. self.dbenvMaster = db.DBEnv()
  18. self.dbenvClient = db.DBEnv()
  19. # Must use "DB_THREAD" because the Replication Manager will
  20. # be executed in other threads but will use the same environment.
  21. # http://forums.oracle.com/forums/thread.jspa?threadID=645788&tstart=0
  22. self.dbenvMaster.open(self.homeDirMaster, db.DB_CREATE | db.DB_INIT_TXN
  23. | db.DB_INIT_LOG | db.DB_INIT_MPOOL | db.DB_INIT_LOCK |
  24. db.DB_INIT_REP | db.DB_RECOVER | db.DB_THREAD, 0666)
  25. self.dbenvClient.open(self.homeDirClient, db.DB_CREATE | db.DB_INIT_TXN
  26. | db.DB_INIT_LOG | db.DB_INIT_MPOOL | db.DB_INIT_LOCK |
  27. db.DB_INIT_REP | db.DB_RECOVER | db.DB_THREAD, 0666)
  28. self.confirmed_master=self.client_startupdone=False
  29. def confirmed_master(a,b,c) :
  30. if b==db.DB_EVENT_REP_MASTER :
  31. self.confirmed_master=True
  32. def client_startupdone(a,b,c) :
  33. if b==db.DB_EVENT_REP_STARTUPDONE :
  34. self.client_startupdone=True
  35. self.dbenvMaster.set_event_notify(confirmed_master)
  36. self.dbenvClient.set_event_notify(client_startupdone)
  37. #self.dbenvMaster.set_verbose(db.DB_VERB_REPLICATION, True)
  38. #self.dbenvMaster.set_verbose(db.DB_VERB_FILEOPS_ALL, True)
  39. #self.dbenvClient.set_verbose(db.DB_VERB_REPLICATION, True)
  40. #self.dbenvClient.set_verbose(db.DB_VERB_FILEOPS_ALL, True)
  41. self.dbMaster = self.dbClient = None
  42. def tearDown(self):
  43. if self.dbClient :
  44. self.dbClient.close()
  45. if self.dbMaster :
  46. self.dbMaster.close()
  47. self.dbenvClient.close()
  48. self.dbenvMaster.close()
  49. test_support.rmtree(self.homeDirClient)
  50. test_support.rmtree(self.homeDirMaster)
  51. def test01_basic_replication(self) :
  52. master_port = test_support.find_unused_port()
  53. self.dbenvMaster.repmgr_set_local_site("127.0.0.1", master_port)
  54. client_port = test_support.find_unused_port()
  55. self.dbenvClient.repmgr_set_local_site("127.0.0.1", client_port)
  56. self.dbenvMaster.repmgr_add_remote_site("127.0.0.1", client_port)
  57. self.dbenvClient.repmgr_add_remote_site("127.0.0.1", master_port)
  58. self.dbenvMaster.rep_set_nsites(2)
  59. self.dbenvClient.rep_set_nsites(2)
  60. self.dbenvMaster.rep_set_priority(10)
  61. self.dbenvClient.rep_set_priority(0)
  62. self.dbenvMaster.rep_set_timeout(db.DB_REP_CONNECTION_RETRY,100123)
  63. self.dbenvClient.rep_set_timeout(db.DB_REP_CONNECTION_RETRY,100321)
  64. self.assertEquals(self.dbenvMaster.rep_get_timeout(
  65. db.DB_REP_CONNECTION_RETRY), 100123)
  66. self.assertEquals(self.dbenvClient.rep_get_timeout(
  67. db.DB_REP_CONNECTION_RETRY), 100321)
  68. self.dbenvMaster.rep_set_timeout(db.DB_REP_ELECTION_TIMEOUT, 100234)
  69. self.dbenvClient.rep_set_timeout(db.DB_REP_ELECTION_TIMEOUT, 100432)
  70. self.assertEquals(self.dbenvMaster.rep_get_timeout(
  71. db.DB_REP_ELECTION_TIMEOUT), 100234)
  72. self.assertEquals(self.dbenvClient.rep_get_timeout(
  73. db.DB_REP_ELECTION_TIMEOUT), 100432)
  74. self.dbenvMaster.rep_set_timeout(db.DB_REP_ELECTION_RETRY, 100345)
  75. self.dbenvClient.rep_set_timeout(db.DB_REP_ELECTION_RETRY, 100543)
  76. self.assertEquals(self.dbenvMaster.rep_get_timeout(
  77. db.DB_REP_ELECTION_RETRY), 100345)
  78. self.assertEquals(self.dbenvClient.rep_get_timeout(
  79. db.DB_REP_ELECTION_RETRY), 100543)
  80. self.dbenvMaster.repmgr_set_ack_policy(db.DB_REPMGR_ACKS_ALL)
  81. self.dbenvClient.repmgr_set_ack_policy(db.DB_REPMGR_ACKS_ALL)
  82. self.dbenvMaster.repmgr_start(1, db.DB_REP_MASTER);
  83. self.dbenvClient.repmgr_start(1, db.DB_REP_CLIENT);
  84. self.assertEquals(self.dbenvMaster.rep_get_nsites(),2)
  85. self.assertEquals(self.dbenvClient.rep_get_nsites(),2)
  86. self.assertEquals(self.dbenvMaster.rep_get_priority(),10)
  87. self.assertEquals(self.dbenvClient.rep_get_priority(),0)
  88. self.assertEquals(self.dbenvMaster.repmgr_get_ack_policy(),
  89. db.DB_REPMGR_ACKS_ALL)
  90. self.assertEquals(self.dbenvClient.repmgr_get_ack_policy(),
  91. db.DB_REPMGR_ACKS_ALL)
  92. # The timeout is necessary in BDB 4.5, since DB_EVENT_REP_STARTUPDONE
  93. # is not generated if the master has no new transactions.
  94. # This is solved in BDB 4.6 (#15542).
  95. import time
  96. timeout = time.time()+10
  97. while (time.time()<timeout) and not (self.confirmed_master and self.client_startupdone) :
  98. time.sleep(0.02)
  99. # this fails on Windows as self.client_startupdone never gets set
  100. # to True - see bug 3892. BUT - even though this assertion
  101. # fails on Windows the rest of the test passes - so to prove
  102. # that we let the rest of the test run. Sadly we can't
  103. # make use of raising TestSkipped() here (unittest still
  104. # reports it as an error), so we yell to stderr.
  105. import sys
  106. if sys.platform=="win32":
  107. print >> sys.stderr, \
  108. "XXX - windows bsddb replication fails on windows and is skipped"
  109. print >> sys.stderr, "XXX - Please see issue #3892"
  110. else:
  111. self.assertTrue(time.time()<timeout)
  112. d = self.dbenvMaster.repmgr_site_list()
  113. self.assertEquals(len(d), 1)
  114. self.assertEquals(d[0][0], "127.0.0.1")
  115. self.assertEquals(d[0][1], client_port)
  116. self.assertTrue((d[0][2]==db.DB_REPMGR_CONNECTED) or \
  117. (d[0][2]==db.DB_REPMGR_DISCONNECTED))
  118. d = self.dbenvClient.repmgr_site_list()
  119. self.assertEquals(len(d), 1)
  120. self.assertEquals(d[0][0], "127.0.0.1")
  121. self.assertEquals(d[0][1], master_port)
  122. self.assertTrue((d[0][2]==db.DB_REPMGR_CONNECTED) or \
  123. (d[0][2]==db.DB_REPMGR_DISCONNECTED))
  124. if db.version() >= (4,6) :
  125. d = self.dbenvMaster.repmgr_stat(flags=db.DB_STAT_CLEAR);
  126. self.assertTrue("msgs_queued" in d)
  127. self.dbMaster=db.DB(self.dbenvMaster)
  128. txn=self.dbenvMaster.txn_begin()
  129. self.dbMaster.open("test", db.DB_HASH, db.DB_CREATE, 0666, txn=txn)
  130. txn.commit()
  131. import time,os.path
  132. timeout=time.time()+10
  133. while (time.time()<timeout) and \
  134. not (os.path.exists(os.path.join(self.homeDirClient,"test"))) :
  135. time.sleep(0.01)
  136. self.dbClient=db.DB(self.dbenvClient)
  137. while True :
  138. txn=self.dbenvClient.txn_begin()
  139. try :
  140. self.dbClient.open("test", db.DB_HASH, flags=db.DB_RDONLY,
  141. mode=0666, txn=txn)
  142. except db.DBRepHandleDeadError :
  143. txn.abort()
  144. self.dbClient.close()
  145. self.dbClient=db.DB(self.dbenvClient)
  146. continue
  147. txn.commit()
  148. break
  149. txn=self.dbenvMaster.txn_begin()
  150. self.dbMaster.put("ABC", "123", txn=txn)
  151. txn.commit()
  152. import time
  153. timeout=time.time()+10
  154. v=None
  155. while (time.time()<timeout) and (v==None) :
  156. txn=self.dbenvClient.txn_begin()
  157. v=self.dbClient.get("ABC", txn=txn)
  158. txn.commit()
  159. if v==None :
  160. time.sleep(0.02)
  161. self.assertTrue(time.time()<timeout)
  162. self.assertEquals("123", v)
  163. txn=self.dbenvMaster.txn_begin()
  164. self.dbMaster.delete("ABC", txn=txn)
  165. txn.commit()
  166. timeout=time.time()+10
  167. while (time.time()<timeout) and (v!=None) :
  168. txn=self.dbenvClient.txn_begin()
  169. v=self.dbClient.get("ABC", txn=txn)
  170. txn.commit()
  171. if v==None :
  172. time.sleep(0.02)
  173. self.assertTrue(time.time()<timeout)
  174. self.assertEquals(None, v)
  175. class DBBaseReplication(DBReplicationManager):
  176. def setUp(self) :
  177. DBReplicationManager.setUp(self)
  178. def confirmed_master(a,b,c) :
  179. if (b == db.DB_EVENT_REP_MASTER) or (b == db.DB_EVENT_REP_ELECTED) :
  180. self.confirmed_master = True
  181. def client_startupdone(a,b,c) :
  182. if b == db.DB_EVENT_REP_STARTUPDONE :
  183. self.client_startupdone = True
  184. self.dbenvMaster.set_event_notify(confirmed_master)
  185. self.dbenvClient.set_event_notify(client_startupdone)
  186. import Queue
  187. self.m2c = Queue.Queue()
  188. self.c2m = Queue.Queue()
  189. # There are only two nodes, so we don't need to
  190. # do any routing decision
  191. def m2c(dbenv, control, rec, lsnp, envid, flags) :
  192. self.m2c.put((control, rec))
  193. def c2m(dbenv, control, rec, lsnp, envid, flags) :
  194. self.c2m.put((control, rec))
  195. self.dbenvMaster.rep_set_transport(13,m2c)
  196. self.dbenvMaster.rep_set_priority(10)
  197. self.dbenvClient.rep_set_transport(3,c2m)
  198. self.dbenvClient.rep_set_priority(0)
  199. self.assertEquals(self.dbenvMaster.rep_get_priority(),10)
  200. self.assertEquals(self.dbenvClient.rep_get_priority(),0)
  201. #self.dbenvMaster.set_verbose(db.DB_VERB_REPLICATION, True)
  202. #self.dbenvMaster.set_verbose(db.DB_VERB_FILEOPS_ALL, True)
  203. #self.dbenvClient.set_verbose(db.DB_VERB_REPLICATION, True)
  204. #self.dbenvClient.set_verbose(db.DB_VERB_FILEOPS_ALL, True)
  205. def thread_master() :
  206. return self.thread_do(self.dbenvMaster, self.c2m, 3,
  207. self.master_doing_election, True)
  208. def thread_client() :
  209. return self.thread_do(self.dbenvClient, self.m2c, 13,
  210. self.client_doing_election, False)
  211. from threading import Thread
  212. t_m=Thread(target=thread_master)
  213. t_c=Thread(target=thread_client)
  214. import sys
  215. if sys.version_info[0] < 3 :
  216. t_m.setDaemon(True)
  217. t_c.setDaemon(True)
  218. else :
  219. t_m.daemon = True
  220. t_c.daemon = True
  221. self.t_m = t_m
  222. self.t_c = t_c
  223. self.dbMaster = self.dbClient = None
  224. self.master_doing_election=[False]
  225. self.client_doing_election=[False]
  226. def tearDown(self):
  227. if self.dbClient :
  228. self.dbClient.close()
  229. if self.dbMaster :
  230. self.dbMaster.close()
  231. self.m2c.put(None)
  232. self.c2m.put(None)
  233. self.t_m.join()
  234. self.t_c.join()
  235. self.dbenvClient.close()
  236. self.dbenvMaster.close()
  237. test_support.rmtree(self.homeDirClient)
  238. test_support.rmtree(self.homeDirMaster)
  239. def basic_rep_threading(self) :
  240. self.dbenvMaster.rep_start(flags=db.DB_REP_MASTER)
  241. self.dbenvClient.rep_start(flags=db.DB_REP_CLIENT)
  242. def thread_do(env, q, envid, election_status, must_be_master) :
  243. while True :
  244. v=q.get()
  245. if v == None : return
  246. env.rep_process_message(v[0], v[1], envid)
  247. self.thread_do = thread_do
  248. self.t_m.start()
  249. self.t_c.start()
  250. def test01_basic_replication(self) :
  251. self.basic_rep_threading()
  252. # The timeout is necessary in BDB 4.5, since DB_EVENT_REP_STARTUPDONE
  253. # is not generated if the master has no new transactions.
  254. # This is solved in BDB 4.6 (#15542).
  255. import time
  256. timeout = time.time()+10
  257. while (time.time()<timeout) and not (self.confirmed_master and
  258. self.client_startupdone) :
  259. time.sleep(0.02)
  260. self.assertTrue(time.time()<timeout)
  261. self.dbMaster=db.DB(self.dbenvMaster)
  262. txn=self.dbenvMaster.txn_begin()
  263. self.dbMaster.open("test", db.DB_HASH, db.DB_CREATE, 0666, txn=txn)
  264. txn.commit()
  265. import time,os.path
  266. timeout=time.time()+10
  267. while (time.time()<timeout) and \
  268. not (os.path.exists(os.path.join(self.homeDirClient,"test"))) :
  269. time.sleep(0.01)
  270. self.dbClient=db.DB(self.dbenvClient)
  271. while True :
  272. txn=self.dbenvClient.txn_begin()
  273. try :
  274. self.dbClient.open("test", db.DB_HASH, flags=db.DB_RDONLY,
  275. mode=0666, txn=txn)
  276. except db.DBRepHandleDeadError :
  277. txn.abort()
  278. self.dbClient.close()
  279. self.dbClient=db.DB(self.dbenvClient)
  280. continue
  281. txn.commit()
  282. break
  283. txn=self.dbenvMaster.txn_begin()
  284. self.dbMaster.put("ABC", "123", txn=txn)
  285. txn.commit()
  286. import time
  287. timeout=time.time()+10
  288. v=None
  289. while (time.time()<timeout) and (v==None) :
  290. txn=self.dbenvClient.txn_begin()
  291. v=self.dbClient.get("ABC", txn=txn)
  292. txn.commit()
  293. if v==None :
  294. time.sleep(0.02)
  295. self.assertTrue(time.time()<timeout)
  296. self.assertEquals("123", v)
  297. txn=self.dbenvMaster.txn_begin()
  298. self.dbMaster.delete("ABC", txn=txn)
  299. txn.commit()
  300. timeout=time.time()+10
  301. while (time.time()<timeout) and (v!=None) :
  302. txn=self.dbenvClient.txn_begin()
  303. v=self.dbClient.get("ABC", txn=txn)
  304. txn.commit()
  305. if v==None :
  306. time.sleep(0.02)
  307. self.assertTrue(time.time()<timeout)
  308. self.assertEquals(None, v)
  309. if db.version() >= (4,7) :
  310. def test02_test_request(self) :
  311. self.basic_rep_threading()
  312. (minimum, maximum) = self.dbenvClient.rep_get_request()
  313. self.dbenvClient.rep_set_request(minimum-1, maximum+1)
  314. self.assertEqual(self.dbenvClient.rep_get_request(),
  315. (minimum-1, maximum+1))
  316. if db.version() >= (4,6) :
  317. def test03_master_election(self) :
  318. # Get ready to hold an election
  319. #self.dbenvMaster.rep_start(flags=db.DB_REP_MASTER)
  320. self.dbenvMaster.rep_start(flags=db.DB_REP_CLIENT)
  321. self.dbenvClient.rep_start(flags=db.DB_REP_CLIENT)
  322. def thread_do(env, q, envid, election_status, must_be_master) :
  323. while True :
  324. v=q.get()
  325. if v == None : return
  326. r = env.rep_process_message(v[0],v[1],envid)
  327. if must_be_master and self.confirmed_master :
  328. self.dbenvMaster.rep_start(flags = db.DB_REP_MASTER)
  329. must_be_master = False
  330. if r[0] == db.DB_REP_HOLDELECTION :
  331. def elect() :
  332. while True :
  333. try :
  334. env.rep_elect(2, 1)
  335. election_status[0] = False
  336. break
  337. except db.DBRepUnavailError :
  338. pass
  339. if not election_status[0] and not self.confirmed_master :
  340. from threading import Thread
  341. election_status[0] = True
  342. t=Thread(target=elect)
  343. import sys
  344. if sys.version_info[0] < 3 :
  345. t.setDaemon(True)
  346. else :
  347. t.daemon = True
  348. t.start()
  349. self.thread_do = thread_do
  350. self.t_m.start()
  351. self.t_c.start()
  352. self.dbenvMaster.rep_set_timeout(db.DB_REP_ELECTION_TIMEOUT, 50000)
  353. self.dbenvClient.rep_set_timeout(db.DB_REP_ELECTION_TIMEOUT, 50000)
  354. self.client_doing_election[0] = True
  355. while True :
  356. try :
  357. self.dbenvClient.rep_elect(2, 1)
  358. self.client_doing_election[0] = False
  359. break
  360. except db.DBRepUnavailError :
  361. pass
  362. self.assertTrue(self.confirmed_master)
  363. #----------------------------------------------------------------------
  364. def test_suite():
  365. suite = unittest.TestSuite()
  366. if db.version() >= (4, 6) :
  367. dbenv = db.DBEnv()
  368. try :
  369. dbenv.repmgr_get_ack_policy()
  370. ReplicationManager_available=True
  371. except :
  372. ReplicationManager_available=False
  373. dbenv.close()
  374. del dbenv
  375. if ReplicationManager_available :
  376. suite.addTest(unittest.makeSuite(DBReplicationManager))
  377. if have_threads :
  378. suite.addTest(unittest.makeSuite(DBBaseReplication))
  379. return suite
  380. if __name__ == '__main__':
  381. unittest.main(defaultTest='test_suite')