Opened 12 years ago
Closed 11 years ago
#10636 closed defect (fixed)
TagWikiMacros is NOT thread safe
Reported by: | Jun Omae | Owned by: | Steffen Hoffmann |
---|---|---|---|
Priority: | normal | Component: | TagsPlugin |
Severity: | normal | Keywords: | threadsafe |
Cc: | Trac Release: | 0.12 |
Description
TagWikiMacros
uses instance variables, self.realms
, at tagsplugin/trunk/tractags/macros.py@11934#L160. However, subclasses of Component
create only one instance per Trac environment. Therefore, the component is not thread safe if wsgi.multithread
is True
(e.g. Apache/win32 with mod_wsgi).
Attachments (1)
Change History (17)
comment:1 follow-up: 2 Changed 12 years ago by
comment:2 follow-up: 3 Changed 12 years ago by
Replying to rjollos:
Interesting. Will this be true anywhere that we make an assignment to an instance variable in a macro?
If a subclass of Component
assigns data from a request to the instance variable, that is not thread safe.
In the TagWikiMacros
, it happens at such as rendering [[ListTagged(realms=wiki)]]
and [[ListTagged(realms=ticket)]]
at the same time in wiki pages.
See trac:wiki:TracDev/ComponentArchitecture#Declaringacomponent.
It looks like these two macros may suffer from the same issue:
Yes. The above macro has the same issue.
It seems that it has the another race condition. Poll
should lock the file to dump and load.
comment:3 follow-ups: 4 5 Changed 12 years ago by
Replying to jun66j5:
Replying to rjollos: If a subclass of
Component
assigns data from a request to the instance variable, that is not thread safe. In theTagWikiMacros
, it happens at such as rendering[[ListTagged(realms=wiki)]]
and[[ListTagged(realms=ticket)]]
at the same time in wiki pages.
If I understand correctly then, the instance variable query
will also have this problem. all_realms
will not because it's value will always be the same? ... but isn't there a very small chance that one thread could be writing all_realms
while another is reading it?
If that were true, maybe we could fix the issue by moving the code to the constructor?:
def __init__(self): self.tag_system = TagSystem(self.env) self.all_realms = [p.get_taggable_realm() for p in self.tag_system.tag_providers]
It looks like these two macros may suffer from the same issue:
Yes. The above macro has the same issue.
Thanks, I will make a ticket and plan to fix it ...
It seems that it has the another race condition.
Poll
should lock the file to dump and load.
... and will do the same for this one (though I think the pickling to file should be replaced with storing to the database anyway, and we have an open ticket for that work item IIRC).
comment:4 Changed 12 years ago by
Replying to rjollos:
Replying to jun66j5:
Replying to rjollos: If a subclass of
Component
assigns data from a request to the instance variable, that is not thread safe. ...If that were true, maybe we could fix the issue by moving the code to the constructor?:
...
Exactly what I've been thinking too.
For currently selected realms we still need a thread-safe alternative to self.realms
to propagate the selection between TagWikiMacros
methods. I can't think of something else than r/w from/to a req.session
attribute, but I haven't looked carefully at the code for a while.
Changed 12 years ago by
Attachment: | tagsplugin-t10636-r12375.diff added |
---|
Starting point of patch to fix #10636
comment:5 follow-up: 6 Changed 12 years ago by
Replying to rjollos:
If I understand correctly then, the instance variable
query
will also have this problem.
Right.
all_realms
will not because it's value will always be the same? ... but isn't there a very small chance that one thread could be writingall_realms
while another is reading it?
self.all_realms
doesn't have this problem because it is unused.
If that were true, maybe we could fix the issue by moving the code to the constructor?:
...
I don't think it should move to the constructor. TagSystem(self.env).tag_providers
dynamically depends on the [components]
settings. Also, the moving will not fix this issue....
I created tagsplugin-t10636-r12375.diff. But the unit tests are missing for the macros. We should add the tests before the patch.
comment:6 Changed 12 years ago by
Replying to jun66j5:
all_realms
will not because it's value will always be the same? ... but isn't there a very small chance that one thread could be writingall_realms
while another is reading it?
self.all_realms
doesn't have this problem because it is unused.
I didn't notice it was unused. It seems like it would have been a problem though if it had been used (code where it is used is commented out).
I don't think it should move to the constructor. TagSystem(self.env).tag_providers dynamically depends on the [components] settings. Also, the moving will not fix this issue....
I had only suggested this for the case in which I thought the instance variable all_realms
was used, but I see it is not important now.
Nice usage of partial
! ;)
comment:7 Changed 12 years ago by
I've done some work in #10612 to try to utilize the Trac framework when writing tests for wiki formatting. I appreciate if you have a chance to take a look.
comment:8 follow-up: 9 Changed 11 years ago by
I reworked at https://github.com/jun66j5/trac-tagsplugin/commits/t10636.
comment:9 follow-up: 11 Changed 11 years ago by
Replying to jun66j5:
I reworked at https://github.com/jun66j5/trac-tagsplugin/commits/t10636.
I've just reviewed and tested your changes. The previous draft looked more spectacular using partial
, sure, but this seems not essential. I like the straight-forward clean-up morphing former class variables into method call signature: Well done - as always - and nice catches especially regarding translated macro description (3785c9e) and more consistent return value in WikiTagProvider.get_all_tags()
(443fe05).
As already commented directly to the changesets, I'd like to see these changes committed here, in order to close the tags-0.7dev
development cycle with a final message catalog update.
comment:11 follow-up: 13 Changed 11 years ago by
I've just reviewed and tested your changes.
Thanks for reviewing. I just committed the changes.
Another thing. I've noticed that TagCloud
macro doesn't understand query syntax while adding unit tests. It seems that the macro with only realm
argument works. If that is intended, I think the documentation should be the following.
-
tractags/macros.py
diff --git a/tractags/macros.py b/tractags/macros.py index 811f8a9..49eaeec 100644
a b class TagWikiMacros(TagTemplateProvider): 115 115 Usage: 116 116 117 117 {{{ 118 [[TagCloud( query,caseless_sort=<bool>,mincount=<n>)]]118 [[TagCloud(realm=<realm1|realm2|...>,caseless_sort=<bool>,mincount=<n>)]] 119 119 }}} 120 realm:: 121 Optional realms to show only tags in the specified realms. 120 122 caseless_sort:: 121 123 Whether the tag cloud should be sorted case-sensitive. 122 124 mincount:: 123 125 Optional integer threshold to hide tags with smaller count. 124 125 See tags documentation for the query syntax.126 126 """) 127 127 self.doc_listtagged = N_("""List tagged resources. 128 128
comment:13 Changed 11 years ago by
Replying to jun66j5:
Another thing. I've noticed that
TagCloud
macro doesn't understand query syntax while adding unit tests. It seems that the macro with onlyrealm
argument works.
This is not intended but a regression coming from [13392].
Further more, finally removing query arguments from get_all_tags() signature in [13427] changed the API in a way, that will make reimplementing the feature less trivial. Looks like we'll have no other choice than to re-introduce some of the very slow code for iterating through tagged resources and corresponding permission checks for constructing custom tag clouds with TagCloud
. A macro documentation change might be in order, but a different one, hinting on serious performance pitfalls when using query arguments other than realm
.
Interesting. Will this be true anywhere that we make an assignment to an instance variable in a macro? It looks like these two macros may suffer from the same issue: