Merge lp:~mvo/software-center/per-thread-xapiandb into lp:software-center

Proposed by Michael Vogt
Status: Merged
Merged at revision: 2331
Proposed branch: lp:~mvo/software-center/per-thread-xapiandb
Merge into: lp:software-center
Diff against target: 155 lines (+64/-41)
2 files modified
softwarecenter/db/database.py (+56/-33)
softwarecenter/db/enquire.py (+8/-8)
To merge this branch: bzr merge lp:~mvo/software-center/per-thread-xapiandb
Reviewer Review Type Date Requested Status
Gary Lasker (community) Approve
Michael Vogt Pending
Review via email: mp+75349@code.launchpad.net

Description of the change

Hopefully kills off race condition behind #736116 (and maybe even #638706)

To post a comment you must log in.
2330. By Michael Vogt

softwarecenter/db/enquire.py: make sure we use uniq names

Revision history for this message
Gary Lasker (gary-lasker) wrote :

Hey mvo! This is a nice and very clever approach, and it certainly does appear to have have excellent potential for taking care of those bugs you mention (and as you know the second of the two, the DocNotFound master bug, is our current most evil one of all).

I did a bunch of testing and this does appear to maintain a unique database instance per each thread, exactly as intended. I could not detect a case where a given thread_name was changed to use a different database.

So, this one seems good to me! If this takes care of the DocNotFound errors, man, that will call for a serious celebration!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'softwarecenter/db/database.py'
2--- softwarecenter/db/database.py 2011-08-17 12:24:39 +0000
3+++ softwarecenter/db/database.py 2011-09-14 16:21:29 +0000
4@@ -140,18 +140,63 @@
5 # the xapian values as read from /var/lib/apt-xapian-index/values
6 self._axi_values = {}
7 self._logger = logging.getLogger("softwarecenter.db")
8-
9- def acquire_search_lock(self):
10- self._search_lock.acquire()
11-
12- def release_search_lock(self):
13- self._search_lock.release()
14-
15+ # we open one db per thread, thread names are reused eventually
16+ # so no memory leak
17+ self._db_per_thread = {}
18+ self._parser_per_thread = {}
19+
20+ @property
21+ def xapiandb(self):
22+ """ returns a per thread db """
23+ thread_name = threading.current_thread().name
24+ if not thread_name in self._db_per_thread:
25+ self._db_per_thread[thread_name] = self._get_new_xapiandb()
26+ return self._db_per_thread[thread_name]
27+
28+ @property
29+ def xapian_parser(self):
30+ """ returns a per thread query parser """
31+ thread_name = threading.current_thread().name
32+ if not thread_name in self._parser_per_thread:
33+ self._parser_per_thread[thread_name] = self._get_new_xapian_parser()
34+ return self._parser_per_thread[thread_name]
35+
36+ def _get_new_xapiandb(self):
37+ xapiandb = xapian.Database(self._db_pathname)
38+ if self._use_axi:
39+ try:
40+ axi = xapian.Database("/var/lib/apt-xapian-index/index")
41+ xapiandb.add_database(axi)
42+ except:
43+ self._logger.exception("failed to add apt-xapian-index")
44+ if (self._use_agent and
45+ os.path.exists(XAPIAN_BASE_PATH_SOFTWARE_CENTER_AGENT)):
46+ try:
47+ sca = xapian.Database(XAPIAN_BASE_PATH_SOFTWARE_CENTER_AGENT)
48+ xapiandb.add_database(sca)
49+ except Exception as e:
50+ logging.warn("failed to add sca db %s" % e)
51+ for db in self._additional_databases:
52+ xapiandb.add_database(db)
53+ return xapiandb
54+
55+ def _get_new_xapian_parser(self):
56+ xapian_parser = xapian.QueryParser()
57+ xapian_parser.set_database(self.xapiandb)
58+ xapian_parser.add_boolean_prefix("pkg", "XP")
59+ xapian_parser.add_boolean_prefix("pkg", "AP")
60+ xapian_parser.add_boolean_prefix("mime", "AM")
61+ xapian_parser.add_boolean_prefix("section", "XS")
62+ xapian_parser.add_boolean_prefix("origin", "XOC")
63+ xapian_parser.add_prefix("pkg_wildcard", "XP")
64+ xapian_parser.add_prefix("pkg_wildcard", "AP")
65+ xapian_parser.set_default_op(xapian.Query.OP_AND)
66+ return xapian_parser
67+
68 def open(self, pathname=None, use_axi=True, use_agent=True):
69 """ open the database """
70 if pathname:
71 self._db_pathname = pathname
72- self.xapiandb = xapian.Database(self._db_pathname)
73 # add the apt-xapian-database for here (we don't do this
74 # for now as we do not have a good way to integrate non-apps
75 # with the UI)
76@@ -159,35 +204,13 @@
77 self._use_axi = use_axi
78 self._use_agent = use_agent
79 if use_axi:
80- try:
81- axi = xapian.Database("/var/lib/apt-xapian-index/index")
82- self.xapiandb.add_database(axi)
83- self._axi_values = parse_axi_values_file()
84- self.nr_databases += 1
85- except:
86- self._logger.exception("failed to add apt-xapian-index")
87+ self._axi_values = parse_axi_values_file()
88+ self.nr_databases += 1
89 if use_agent:
90- try:
91- sca = xapian.Database(XAPIAN_BASE_PATH_SOFTWARE_CENTER_AGENT)
92- self.xapiandb.add_database(sca)
93- self.nr_databases += 1
94- except Exception as e:
95- logging.warn("failed to add sca db %s" % e)
96+ self.nr_databases += 1
97 # additional dbs
98 for db in self._additional_databases:
99- self.xapiandb.add_database(db)
100 self.nr_databases += 1
101- # parser etc
102- self.xapian_parser = xapian.QueryParser()
103- self.xapian_parser.set_database(self.xapiandb)
104- self.xapian_parser.add_boolean_prefix("pkg", "XP")
105- self.xapian_parser.add_boolean_prefix("pkg", "AP")
106- self.xapian_parser.add_boolean_prefix("mime", "AM")
107- self.xapian_parser.add_boolean_prefix("section", "XS")
108- self.xapian_parser.add_boolean_prefix("origin", "XOC")
109- self.xapian_parser.add_prefix("pkg_wildcard", "XP")
110- self.xapian_parser.add_prefix("pkg_wildcard", "AP")
111- self.xapian_parser.set_default_op(xapian.Query.OP_AND)
112 self.emit("open", self._db_pathname)
113
114 def add_database(self, database):
115
116=== modified file 'softwarecenter/db/enquire.py'
117--- softwarecenter/db/enquire.py 2011-08-25 18:51:39 +0000
118+++ softwarecenter/db/enquire.py 2011-09-14 16:21:29 +0000
119@@ -85,7 +85,14 @@
120
121 def _threaded_perform_search(self):
122 self._perform_search_complete = False
123- thread_name = 'ThreadedQuery-%s' % (threading.active_count()+1)
124+ # generate a name and ensure we never have two threads
125+ # with the same name
126+ names = [thread.name for thread in threading.enumerate()]
127+ for i in range(threading.active_count()+1, 0, -1):
128+ thread_name = 'ThreadedQuery-%s' % i
129+ if not thread_name in names:
130+ break
131+ # create and start it
132 t = threading.Thread(
133 target=self._blocking_perform_search, name=thread_name)
134 t.start()
135@@ -128,9 +135,6 @@
136 # use a unique instance of both enquire and xapian database
137 # so concurrent queries dont result in an inconsistent database
138
139- # get lock
140- self.db.acquire_search_lock()
141-
142 # an alternative would be to serialise queries
143 enquire = xapian.Enquire(self.db.xapiandb)
144
145@@ -223,10 +227,6 @@
146 _matches.append(match)
147 match_docids.add(match.docid)
148
149- # release the lock here because the following check may trigger
150- # calling this function again (and we would deadlock otherwise)
151- self.db.release_search_lock()
152-
153 # if we have no results, try forcing pkgs to be displayed
154 # if not NonAppVisibility.NEVER_VISIBLE is set
155 if (not _matches and

Subscribers

People subscribed via source and target branches