Filename | /usr/local/src/github.com/foswiki/core/lib/Foswiki/MetaCache.pm |
Statements | Executed 2877 statements in 12.7ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
67 | 2 | 2 | 3.13ms | 2.81s | addMeta | Foswiki::MetaCache::
45 | 3 | 3 | 3.10ms | 1.19s | get | Foswiki::MetaCache::
189 | 3 | 1 | 2.98ms | 3.58ms | current_user | Foswiki::MetaCache::
77 | 2 | 2 | 1.42ms | 2.95ms | getMeta | Foswiki::MetaCache::
1 | 1 | 1 | 491µs | 491µs | finish | Foswiki::MetaCache::
1 | 1 | 1 | 25µs | 32µs | BEGIN@3 | Foswiki::MetaCache::
1 | 1 | 1 | 22µs | 159µs | BEGIN@28 | Foswiki::MetaCache::
1 | 1 | 1 | 19µs | 19µs | new | Foswiki::MetaCache::
1 | 1 | 1 | 16µs | 53µs | BEGIN@20 | Foswiki::MetaCache::
1 | 1 | 1 | 16µs | 34µs | BEGIN@4 | Foswiki::MetaCache::
1 | 1 | 1 | 9µs | 9µs | BEGIN@23 | Foswiki::MetaCache::
1 | 1 | 1 | 9µs | 9µs | BEGIN@21 | Foswiki::MetaCache::
1 | 1 | 1 | 8µs | 8µs | BEGIN@22 | Foswiki::MetaCache::
0 | 0 | 0 | 0s | 0s | hasCached | Foswiki::MetaCache::
0 | 0 | 0 | 0s | 0s | insert | Foswiki::MetaCache::
0 | 0 | 0 | 0s | 0s | remove | Foswiki::MetaCache::
0 | 0 | 0 | 0s | 0s | removeMeta | Foswiki::MetaCache::
0 | 0 | 0 | 0s | 0s | update | Foswiki::MetaCache::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for license and copyright information | ||||
2 | package Foswiki::MetaCache; | ||||
3 | 2 | 45µs | 2 | 39µs | # spent 32µs (25+7) within Foswiki::MetaCache::BEGIN@3 which was called:
# once (25µs+7µs) by Foswiki::Search::BEGIN@25 at line 3 # spent 32µs making 1 call to Foswiki::MetaCache::BEGIN@3
# spent 7µs making 1 call to strict::import |
4 | 2 | 56µs | 2 | 51µs | # spent 34µs (16+18) within Foswiki::MetaCache::BEGIN@4 which was called:
# once (16µs+18µs) by Foswiki::Search::BEGIN@25 at line 4 # spent 34µs making 1 call to Foswiki::MetaCache::BEGIN@4
# spent 18µs making 1 call to warnings::import |
5 | |||||
6 | =begin TML | ||||
7 | |||||
8 | ---+ package Foswiki::MetaCache | ||||
9 | |||||
10 | A cache of Meta objects - initially used to speed up searching and sorting, but by foswiki 2.0 hopefully this | ||||
11 | will be used for all readonly accesses to the store. | ||||
12 | |||||
13 | Replaces the mishmash of the Search InfoCache Support package; cache of topic info. | ||||
14 | When information about search hits is | ||||
15 | compiled for output, this cache is used to avoid recovering the same info | ||||
16 | about the same topic more than once. | ||||
17 | |||||
18 | =cut | ||||
19 | |||||
20 | 2 | 42µs | 2 | 91µs | # spent 53µs (16+37) within Foswiki::MetaCache::BEGIN@20 which was called:
# once (16µs+37µs) by Foswiki::Search::BEGIN@25 at line 20 # spent 53µs making 1 call to Foswiki::MetaCache::BEGIN@20
# spent 37µs making 1 call to Assert::import |
21 | 2 | 37µs | 1 | 9µs | # spent 9µs within Foswiki::MetaCache::BEGIN@21 which was called:
# once (9µs+0s) by Foswiki::Search::BEGIN@25 at line 21 # spent 9µs making 1 call to Foswiki::MetaCache::BEGIN@21 |
22 | 2 | 37µs | 1 | 8µs | # spent 8µs within Foswiki::MetaCache::BEGIN@22 which was called:
# once (8µs+0s) by Foswiki::Search::BEGIN@25 at line 22 # spent 8µs making 1 call to Foswiki::MetaCache::BEGIN@22 |
23 | 2 | 44µs | 1 | 9µs | # spent 9µs within Foswiki::MetaCache::BEGIN@23 which was called:
# once (9µs+0s) by Foswiki::Search::BEGIN@25 at line 23 # spent 9µs making 1 call to Foswiki::MetaCache::BEGIN@23 |
24 | |||||
25 | #use Monitor (); | ||||
26 | #Monitor::MonitorMethod('Foswiki::MetaCache', 'getTopicListIterator'); | ||||
27 | |||||
28 | 2 | 1.83ms | 2 | 296µs | # spent 159µs (22+137) within Foswiki::MetaCache::BEGIN@28 which was called:
# once (22µs+137µs) by Foswiki::Search::BEGIN@25 at line 28 # spent 159µs making 1 call to Foswiki::MetaCache::BEGIN@28
# spent 137µs making 1 call to constant::import |
29 | |||||
30 | =begin TML | ||||
31 | |||||
32 | ---++ ClassMethod new($session) | ||||
33 | |||||
34 | =cut | ||||
35 | |||||
36 | # spent 19µs within Foswiki::MetaCache::new which was called:
# once (19µs+0s) by Foswiki::Search::metacache at line 98 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Search.pm | ||||
37 | 3 | 23µs | my ( $class, $session ) = @_; | ||
38 | |||||
39 | #my $this = $class->SUPER::new([]); | ||||
40 | my $this = bless( | ||||
41 | { | ||||
42 | session => $session, | ||||
43 | cache => {}, | ||||
44 | new_count => 0, | ||||
45 | get_count => 0, | ||||
46 | undef_count => 0, | ||||
47 | meta_cache_session_user => $session->{user}, | ||||
48 | }, | ||||
49 | $class | ||||
50 | ); | ||||
51 | |||||
52 | return $this; | ||||
53 | } | ||||
54 | |||||
55 | #need to delete from cache if the store saves / updates it :/ | ||||
56 | #otherwise the Meta::load thing is busted. | ||||
57 | |||||
58 | =begin TML | ||||
59 | |||||
60 | ---++ ObjectMethod finish() | ||||
61 | Break circular references. | ||||
62 | |||||
63 | Note to developers; please undef *all* fields in the object explicitly, | ||||
64 | whether they are references or not. That way this method is "golden | ||||
65 | documentation" of the live fields in the object. | ||||
66 | |||||
67 | =cut | ||||
68 | |||||
69 | # spent 491µs within Foswiki::MetaCache::finish which was called:
# once (491µs+0s) by Foswiki::Search::finish at line 81 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Search.pm | ||||
70 | 61 | 493µs | my $this = shift; | ||
71 | undef $this->{session}; | ||||
72 | |||||
73 | #must clear cache every request until the cache is hooked up to Store's save | ||||
74 | foreach my $cuid ( keys( %{ $this->{cache} } ) ) { | ||||
75 | foreach my $web ( keys( %{ $this->{cache}->{$cuid} } ) ) { | ||||
76 | foreach my $topic ( keys( %{ $this->{cache}->{$cuid}->{$web} } ) ) { | ||||
77 | undef $this->{cache}->{$cuid}{$web}{$topic}; | ||||
78 | $this->{undef_count}++; | ||||
79 | } | ||||
80 | undef $this->{cache}->{$cuid}{$web}; | ||||
81 | } | ||||
82 | undef $this->{cache}->{$cuid}; | ||||
83 | } | ||||
84 | undef $this->{cache}; | ||||
85 | |||||
86 | if (TRACE) { | ||||
87 | print STDERR | ||||
88 | "MetaCache: new: $this->{new_count} get: $this->{get_count} undef: $this->{undef_count} \n"; | ||||
89 | } | ||||
90 | |||||
91 | return; | ||||
92 | } | ||||
93 | |||||
94 | =begin TML | ||||
95 | |||||
96 | ---++ ObjectMethod hasCached($webtopic) -> boolean | ||||
97 | |||||
98 | returns true if the topic is already int he cache. | ||||
99 | |||||
100 | =cut | ||||
101 | |||||
102 | sub hasCached { | ||||
103 | my ( $this, $web, $topic ) = @_; | ||||
104 | ASSERT( defined($topic) ) if DEBUG; | ||||
105 | return unless ( defined($topic) ); | ||||
106 | |||||
107 | return ( $this->{session}->{user} | ||||
108 | and defined( $this->{cache}->{ $this->current_user() }{$web}{$topic} ) | ||||
109 | ); | ||||
110 | } | ||||
111 | |||||
112 | sub removeMeta { | ||||
113 | my ( $this, $web, $topic ) = @_; | ||||
114 | my $user = $this->current_user(); | ||||
115 | |||||
116 | if ( defined($topic) and defined( $this->{cache}->{$user}{$web}{$topic} ) ) | ||||
117 | { | ||||
118 | $this->{cache}->{$user}{$web}{$topic}->finish(); | ||||
119 | delete $this->{cache}->{$user}{$web}{$topic}; | ||||
120 | } | ||||
121 | else { | ||||
122 | foreach my $topic ( keys( %{ $this->{cache}->{$user}{$web} } ) ) { | ||||
123 | $this->removeMeta( $web, $topic ); | ||||
124 | } | ||||
125 | delete $this->{cache}->{$user}{$web}; | ||||
126 | } | ||||
127 | |||||
128 | return; | ||||
129 | } | ||||
130 | |||||
131 | #returns undef if the meta is not the latestRev, or if it failed to load | ||||
132 | #else returns the $meta | ||||
133 | # spent 2.81s (3.13ms+2.81) within Foswiki::MetaCache::addMeta which was called 67 times, avg 42.0ms/call:
# 45 times (1.68ms+1.18s) by Foswiki::MetaCache::get at line 203, avg 26.3ms/call
# 22 times (1.45ms+1.63s) by Foswiki::Store::Interfaces::QueryAlgorithm::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm:187] at line 169 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm, avg 74.2ms/call | ||||
134 | 763 | 2.89ms | my ( $this, $web, $topic, $meta ) = @_; | ||
135 | |||||
136 | if ( not defined($meta) ) { | ||||
137 | 23 | 2.81s | $meta = Foswiki::Meta->load( $this->{session}, $web, $topic ); # spent 2.81s making 23 calls to Foswiki::Meta::load, avg 122ms/call | ||
138 | } | ||||
139 | if ( ( defined($meta) and $meta ne '' ) | ||||
140 | and defined( $meta->{_latestIsLoaded} ) | ||||
141 | and defined( $meta->{_loadedRev} ) | ||||
142 | and ( $meta->{_loadedRev} > 0 ) ) | ||||
143 | { | ||||
144 | 67 | 226µs | ASSERT( $meta->{_latestIsLoaded} ) if DEBUG; # spent 226µs making 67 calls to Assert::ASSERTS_OFF, avg 3µs/call | ||
145 | 67 | 206µs | ASSERT( defined( $meta->{_loadedRev} ) and ( $meta->{_loadedRev} > 0 ) ) # spent 206µs making 67 calls to Assert::ASSERTS_OFF, avg 3µs/call | ||
146 | if DEBUG; | ||||
147 | } | ||||
148 | else { | ||||
149 | return; | ||||
150 | } | ||||
151 | |||||
152 | 67 | 1.20ms | my $user = $this->current_user(); # spent 1.20ms making 67 calls to Foswiki::MetaCache::current_user, avg 18µs/call | ||
153 | |||||
154 | unless ( $this->{cache}->{$user}{$web} ) { | ||||
155 | $this->{cache}->{$user}{$web} = {}; | ||||
156 | } | ||||
157 | unless ( $this->{cache}->{$user}{$web}{$topic} ) { | ||||
158 | $this->{cache}->{$user}{$web}{$topic} = {}; | ||||
159 | } | ||||
160 | unless ( defined( $this->{cache}->{$user}{$web}{$topic}->{tom} ) ) { | ||||
161 | $this->{cache}->{$user}{$web}{$topic}->{tom} = $meta; | ||||
162 | $this->{new_count}++; | ||||
163 | } | ||||
164 | return $meta; | ||||
165 | } | ||||
166 | |||||
167 | # spent 2.95ms (1.42+1.54) within Foswiki::MetaCache::getMeta which was called 77 times, avg 38µs/call:
# 55 times (982µs+1.16ms) by Foswiki::Meta::load at line 468 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Meta.pm, avg 39µs/call
# 22 times (435µs+380µs) by Foswiki::Search::formatResults at line 736 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Search.pm, avg 37µs/call | ||||
168 | 297 | 1.39ms | my ( $this, $web, $topic, $meta ) = @_; | ||
169 | 77 | 1.54ms | my $user = $this->current_user(); # spent 1.54ms making 77 calls to Foswiki::MetaCache::current_user, avg 20µs/call | ||
170 | |||||
171 | return unless ( defined( $this->{cache}->{$user}{$web} ) ); | ||||
172 | return unless ( defined( $this->{cache}->{$user}{$web}{$topic} ) ); | ||||
173 | return $this->{cache}->{$user}{$web}{$topic}->{tom}; | ||||
174 | } | ||||
175 | |||||
176 | =begin TML | ||||
177 | |||||
178 | ---++ ObjectMethod get($web, $topic, $meta) -> a cache obj (sorry, needs to be refactored out to return a Foswiki::Meta obj only | ||||
179 | |||||
180 | get a requested meta object - web or topic typically, might work for attachments too | ||||
181 | |||||
182 | optionally the $meta parameter can be used to add that to the cache - useful if you've already loaded and parsed the topic. | ||||
183 | |||||
184 | |||||
185 | TODO: the non-meta SEARCH render specific bits need to be moved elsewhere | ||||
186 | and then, the MetaCache can only return Meta objects that actually exist | ||||
187 | |||||
188 | =cut | ||||
189 | |||||
190 | # spent 1.19s (3.10ms+1.19) within Foswiki::MetaCache::get which was called 45 times, avg 26.4ms/call:
# 22 times (1.92ms+5.59ms) by Foswiki::Store::Interfaces::QueryAlgorithm::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm:187] at line 179 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Store/Interfaces/QueryAlgorithm.pm, avg 341µs/call
# 22 times (1.04ms+1.89ms) by Foswiki::Search::formatResults at line 743 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Search.pm, avg 133µs/call
# once (143µs+1.18s) by Foswiki::Store::QueryAlgorithms::BruteForce::_webQuery at line 78 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Store/QueryAlgorithms/BruteForce.pm | ||||
191 | 792 | 2.89ms | my ( $this, $web, $topic, $meta ) = @_; | ||
192 | 44 | 141µs | ASSERT( $meta->isa('Foswiki::Meta') ) if ( defined($meta) and DEBUG ); # spent 141µs making 44 calls to Assert::ASSERTS_OFF, avg 3µs/call | ||
193 | |||||
194 | #sadly, Search.pm actually beleives that it can send out for info on Meta objects that do not exist | ||||
195 | #ASSERT( defined($meta->{_loadedRev}) ) if ( defined($meta) and DEBUG ); | ||||
196 | |||||
197 | if ( !defined($topic) ) { | ||||
198 | |||||
199 | #there are some instances - like the result set sorting, where we need to quickly pass "$web.$topic" | ||||
200 | ( $web, $topic ) = Foswiki::Func::normalizeWebTopicName( '', $web ); | ||||
201 | } | ||||
202 | |||||
203 | 45 | 1.18s | my $m = $this->addMeta( $web, $topic, $meta ); # spent 1.18s making 45 calls to Foswiki::MetaCache::addMeta, avg 26.3ms/call | ||
204 | $meta = $m if ( defined($m) ); | ||||
205 | 45 | 154µs | ASSERT( defined($meta) ) if DEBUG; # spent 154µs making 45 calls to Assert::ASSERTS_OFF, avg 3µs/call | ||
206 | |||||
207 | $this->{get_count}++; | ||||
208 | |||||
209 | my $info = { tom => $meta }; | ||||
210 | 45 | 842µs | my $user = $this->current_user(); # spent 842µs making 45 calls to Foswiki::MetaCache::current_user, avg 19µs/call | ||
211 | |||||
212 | 45 | 159µs | ASSERT( defined $user ) if DEBUG; # spent 159µs making 45 calls to Assert::ASSERTS_OFF, avg 4µs/call | ||
213 | $info = $this->{cache}->{$user}{$web}{$topic} | ||||
214 | if defined( $this->{cache}->{$user}{$web}{$topic} ); | ||||
215 | if ( not defined( $info->{editby} ) ) { | ||||
216 | |||||
217 | #TODO: extract this to the Meta Class, or remove entirely | ||||
218 | # Extract sort fields | ||||
219 | 23 | 1.28ms | my $ri = $info->{tom}->getRevisionInfo(); # spent 1.28ms making 23 calls to Foswiki::Meta::getRevisionInfo, avg 56µs/call | ||
220 | |||||
221 | # Rename fields to match sorting criteria | ||||
222 | $info->{editby} = $ri->{author} || ''; | ||||
223 | $info->{modified} = $ri->{date}; | ||||
224 | $info->{revNum} = $ri->{version}; | ||||
225 | |||||
226 | #TODO: this is _not_ actually sufficient.. as there are other things that appear to be evaluated in turn | ||||
227 | #Ideally, the Store2::Meta object will _not_ contain any session info, and anything that is session / user oriented gets stored in another object that links to the 'database' object. | ||||
228 | #it'll probably be better to make the MetaCache know what | ||||
229 | #Item10097: make the cache multi-user safe by storing the haveAccess on a per user basis | ||||
230 | if ( not defined( $info->{ $this->{session}->{user} } ) ) { | ||||
231 | $info->{ $this->{session}->{user} } = (); | ||||
232 | } | ||||
233 | if ( not defined( $info->{ $this->{session}->{user} }{allowView} ) ) { | ||||
234 | 23 | 2.60ms | $info->{ $this->{session}->{user} }{allowView} = # spent 2.60ms making 23 calls to Foswiki::Meta::haveAccess, avg 113µs/call | ||
235 | $info->{tom}->haveAccess('VIEW'); | ||||
236 | } | ||||
237 | |||||
238 | #use the cached permission | ||||
239 | $info->{allowView} = $info->{ $this->{session}->{user} }{allowView}; | ||||
240 | } | ||||
241 | |||||
242 | return $info; | ||||
243 | } | ||||
244 | |||||
245 | ########################################## | ||||
246 | # use the Listener API to detect when to flush the cache | ||||
247 | #magically enable it. | ||||
248 | |||||
249 | 1 | 4µs | $Foswiki::cfg{Store}{Listeners}{'Foswiki::MetaCache'} = 1; | ||
250 | |||||
251 | sub insert { | ||||
252 | my ( $self, %args ) = @_; | ||||
253 | |||||
254 | $self->removeMeta( $args{newmeta}->web, $args{newmeta}->topic ); | ||||
255 | |||||
256 | return; | ||||
257 | } | ||||
258 | |||||
259 | sub update { | ||||
260 | my ( $self, %args ) = @_; | ||||
261 | |||||
262 | $self->removeMeta( $args{oldmeta}->web, $args{oldmeta}->topic ) | ||||
263 | if ( defined( $args{oldmeta} ) ); | ||||
264 | $self->removeMeta( $args{newmeta}->web, $args{newmeta}->topic ); | ||||
265 | |||||
266 | return; | ||||
267 | } | ||||
268 | |||||
269 | sub remove { | ||||
270 | my ( $self, %args ) = @_; | ||||
271 | |||||
272 | ASSERT( $args{oldmeta} ) if DEBUG; | ||||
273 | |||||
274 | $self->removeMeta( $args{oldmeta}->web, $args{oldmeta}->topic ) | ||||
275 | if ( defined( $args{oldmeta} ) ); | ||||
276 | |||||
277 | return; | ||||
278 | } | ||||
279 | |||||
280 | # spent 3.58ms (2.98+601µs) within Foswiki::MetaCache::current_user which was called 189 times, avg 19µs/call:
# 77 times (1.27ms+264µs) by Foswiki::MetaCache::getMeta at line 169, avg 20µs/call
# 67 times (1.00ms+200µs) by Foswiki::MetaCache::addMeta at line 152, avg 18µs/call
# 45 times (705µs+137µs) by Foswiki::MetaCache::get at line 210, avg 19µs/call | ||||
281 | 945 | 2.88ms | my $self = shift; | ||
282 | |||||
283 | 189 | 601µs | ASSERT( defined $self->{session} ) if DEBUG; # spent 601µs making 189 calls to Assert::ASSERTS_OFF, avg 3µs/call | ||
284 | my $user = $self->{session}->{user}; | ||||
285 | if ( not defined $user ) { | ||||
286 | $user = $Foswiki::Users::BaseUserMapping::UNKNOWN_USER_CUID; | ||||
287 | } | ||||
288 | |||||
289 | return $user; | ||||
290 | } | ||||
291 | |||||
292 | 1 | 6µs | 1; | ||
293 | __END__ |