|
1 | 1 | import datetime
|
| 2 | +import Queue |
| 3 | +from threading import Thread |
| 4 | +import time |
2 | 5 | from django.test import TestCase
|
3 | 6 | from haystack.indexes import *
|
4 | 7 | from core.models import MockModel, AThirdMockModel
|
@@ -195,6 +198,54 @@ def test_custom_prepare(self):
|
195 | 198 | self.assertEqual(len(self.cmi.full_prepare(mock)), 11)
|
196 | 199 | self.assertEqual(sorted(self.cmi.full_prepare(mock).keys()), ['author', 'author_exact', 'content', 'django_ct', 'django_id', 'extra', 'hello', 'id', 'pub_date', 'pub_date_exact', 'whee'])
|
197 | 200 |
|
| 201 | + def test_thread_safety(self): |
| 202 | + # This is a regression. ``SearchIndex`` used to write to |
| 203 | + # ``self.prepared_data``, which would leak between threads if things |
| 204 | + # went too fast. |
| 205 | + exceptions = [] |
| 206 | + |
| 207 | + def threaded_prepare(queue, index, model): |
| 208 | + try: |
| 209 | + index.queue = queue |
| 210 | + prepped = index.prepare(model) |
| 211 | + except Exception, e: |
| 212 | + exceptions.append(e) |
| 213 | + raise |
| 214 | + |
| 215 | + class ThreadedSearchIndex(GoodMockSearchIndex): |
| 216 | + def prepare_author(self, obj): |
| 217 | + if obj.pk == 20: |
| 218 | + time.sleep(0.1) |
| 219 | + else: |
| 220 | + time.sleep(0.5) |
| 221 | + |
| 222 | + queue.put(self.prepared_data['author']) |
| 223 | + return self.prepared_data['author'] |
| 224 | + |
| 225 | + tmi = ThreadedSearchIndex(MockModel, backend=self.msb) |
| 226 | + queue = Queue.Queue() |
| 227 | + mock_1 = MockModel() |
| 228 | + mock_1.pk = 20 |
| 229 | + mock_1.author = 'foo' |
| 230 | + mock_1.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0) |
| 231 | + mock_2 = MockModel() |
| 232 | + mock_2.pk = 21 |
| 233 | + mock_2.author = 'daniel%s' % mock_2.id |
| 234 | + mock_2.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0) |
| 235 | + |
| 236 | + th1 = Thread(target=threaded_prepare, args=(queue, tmi, mock_1)) |
| 237 | + th2 = Thread(target=threaded_prepare, args=(queue, tmi, mock_2)) |
| 238 | + |
| 239 | + th1.start() |
| 240 | + th2.start() |
| 241 | + th1.join() |
| 242 | + th2.join() |
| 243 | + |
| 244 | + mock_1_result = queue.get() |
| 245 | + mock_2_result = queue.get() |
| 246 | + self.assertEqual(mock_1_result, u'foo') |
| 247 | + self.assertEqual(mock_2_result, u'daniel21') |
| 248 | + |
198 | 249 | def test_custom_prepare_author(self):
|
199 | 250 | mock = MockModel()
|
200 | 251 | mock.pk = 20
|
|
0 commit comments