##// END OF EJS Templates
wireprotov2: support response caching...
Gregory Szorc -
r40057:c537144f default
parent child Browse files
Show More
This diff has been collapsed as it changes many lines, (645 lines changed) Show them Hide them
@@ -0,0 +1,645 b''
1 $ . $TESTDIR/wireprotohelpers.sh
2 $ cat >> $HGRCPATH << EOF
3 > [extensions]
4 > blackbox =
5 > [blackbox]
6 > track = simplecache
7 > EOF
8 $ hg init server
9 $ enablehttpv2 server
10 $ cd server
11 $ cat >> .hg/hgrc << EOF
12 > [extensions]
13 > simplecache = $TESTDIR/wireprotosimplecache.py
14 > EOF
15
16 $ echo a0 > a
17 $ echo b0 > b
18 $ hg -q commit -A -m 'commit 0'
19 $ echo a1 > a
20 $ hg commit -m 'commit 1'
21 $ echo b1 > b
22 $ hg commit -m 'commit 2'
23 $ echo a2 > a
24 $ echo b2 > b
25 $ hg commit -m 'commit 3'
26
27 $ hg log -G -T '{rev}:{node} {desc}'
28 @ 3:50590a86f3ff5d1e9a1624a7a6957884565cc8e8 commit 3
29 |
30 o 2:4d01eda50c6ac5f7e89cbe1880143a32f559c302 commit 2
31 |
32 o 1:4432d83626e8a98655f062ec1f2a43b07f7fbbb0 commit 1
33 |
34 o 0:3390ef850073fbc2f0dfff2244342c8e9229013a commit 0
35
36
37 $ hg --debug debugindex -m
38 rev linkrev nodeid p1 p2
39 0 0 992f4779029a3df8d0666d00bb924f69634e2641 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000
40 1 1 a988fb43583e871d1ed5750ee074c6d840bbbfc8 992f4779029a3df8d0666d00bb924f69634e2641 0000000000000000000000000000000000000000
41 2 2 a8853dafacfca6fc807055a660d8b835141a3bb4 a988fb43583e871d1ed5750ee074c6d840bbbfc8 0000000000000000000000000000000000000000
42 3 3 3fe11dfbb13645782b0addafbe75a87c210ffddc a8853dafacfca6fc807055a660d8b835141a3bb4 0000000000000000000000000000000000000000
43
44 $ hg serve -p $HGPORT -d --pid-file hg.pid -E error.log
45 $ cat hg.pid > $DAEMON_PIDS
46
47 Performing the same request should result in same result, with 2nd response
48 coming from cache.
49
50 $ sendhttpv2peer << EOF
51 > command manifestdata
52 > nodes eval:[b'\x99\x2f\x47\x79\x02\x9a\x3d\xf8\xd0\x66\x6d\x00\xbb\x92\x4f\x69\x63\x4e\x26\x41']
53 > tree eval:b''
54 > fields eval:[b'parents']
55 > EOF
56 creating http peer for wire protocol version 2
57 sending manifestdata command
58 s> POST /api/exp-http-v2-0002/ro/manifestdata HTTP/1.1\r\n
59 s> Accept-Encoding: identity\r\n
60 s> accept: application/mercurial-exp-framing-0005\r\n
61 s> content-type: application/mercurial-exp-framing-0005\r\n
62 s> content-length: 83\r\n
63 s> host: $LOCALIP:$HGPORT\r\n (glob)
64 s> user-agent: Mercurial debugwireproto\r\n
65 s> \r\n
66 s> K\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa3Ffields\x81GparentsEnodes\x81T\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&ADtree@DnameLmanifestdata
67 s> makefile('rb', None)
68 s> HTTP/1.1 200 OK\r\n
69 s> Server: testing stub value\r\n
70 s> Date: $HTTP_DATE$\r\n
71 s> Content-Type: application/mercurial-exp-framing-0005\r\n
72 s> Transfer-Encoding: chunked\r\n
73 s> \r\n
74 s> 13\r\n
75 s> \x0b\x00\x00\x01\x00\x02\x011
76 s> \xa1FstatusBok
77 s> \r\n
78 received frame(size=11; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=continuation)
79 s> 63\r\n
80 s> [\x00\x00\x01\x00\x02\x001
81 s> \xa1Jtotalitems\x01\xa2DnodeT\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&AGparents\x82T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
82 s> \r\n
83 received frame(size=91; request=1; stream=2; streamflags=; type=command-response; flags=continuation)
84 s> 8\r\n
85 s> \x00\x00\x00\x01\x00\x02\x002
86 s> \r\n
87 s> 0\r\n
88 s> \r\n
89 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
90 response: gen[
91 {
92 b'totalitems': 1
93 },
94 {
95 b'node': b'\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
96 b'parents': [
97 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
98 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
99 ]
100 }
101 ]
102
103 $ sendhttpv2peer << EOF
104 > command manifestdata
105 > nodes eval:[b'\x99\x2f\x47\x79\x02\x9a\x3d\xf8\xd0\x66\x6d\x00\xbb\x92\x4f\x69\x63\x4e\x26\x41']
106 > tree eval:b''
107 > fields eval:[b'parents']
108 > EOF
109 creating http peer for wire protocol version 2
110 sending manifestdata command
111 s> POST /api/exp-http-v2-0002/ro/manifestdata HTTP/1.1\r\n
112 s> Accept-Encoding: identity\r\n
113 s> accept: application/mercurial-exp-framing-0005\r\n
114 s> content-type: application/mercurial-exp-framing-0005\r\n
115 s> content-length: 83\r\n
116 s> host: $LOCALIP:$HGPORT\r\n (glob)
117 s> user-agent: Mercurial debugwireproto\r\n
118 s> \r\n
119 s> K\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa3Ffields\x81GparentsEnodes\x81T\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&ADtree@DnameLmanifestdata
120 s> makefile('rb', None)
121 s> HTTP/1.1 200 OK\r\n
122 s> Server: testing stub value\r\n
123 s> Date: $HTTP_DATE$\r\n
124 s> Content-Type: application/mercurial-exp-framing-0005\r\n
125 s> Transfer-Encoding: chunked\r\n
126 s> \r\n
127 s> 13\r\n
128 s> \x0b\x00\x00\x01\x00\x02\x011
129 s> \xa1FstatusBok
130 s> \r\n
131 received frame(size=11; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=continuation)
132 s> 63\r\n
133 s> [\x00\x00\x01\x00\x02\x001
134 s> \xa1Jtotalitems\x01\xa2DnodeT\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&AGparents\x82T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
135 s> \r\n
136 received frame(size=91; request=1; stream=2; streamflags=; type=command-response; flags=continuation)
137 s> 8\r\n
138 s> \x00\x00\x00\x01\x00\x02\x002
139 s> \r\n
140 s> 0\r\n
141 s> \r\n
142 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
143 response: gen[
144 {
145 b'totalitems': 1
146 },
147 {
148 b'node': b'\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
149 b'parents': [
150 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
151 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
152 ]
153 }
154 ]
155
156 Sending different request doesn't yield cache hit.
157
158 $ sendhttpv2peer << EOF
159 > command manifestdata
160 > nodes eval:[b'\x99\x2f\x47\x79\x02\x9a\x3d\xf8\xd0\x66\x6d\x00\xbb\x92\x4f\x69\x63\x4e\x26\x41', b'\xa9\x88\xfb\x43\x58\x3e\x87\x1d\x1e\xd5\x75\x0e\xe0\x74\xc6\xd8\x40\xbb\xbf\xc8']
161 > tree eval:b''
162 > fields eval:[b'parents']
163 > EOF
164 creating http peer for wire protocol version 2
165 sending manifestdata command
166 s> POST /api/exp-http-v2-0002/ro/manifestdata HTTP/1.1\r\n
167 s> Accept-Encoding: identity\r\n
168 s> accept: application/mercurial-exp-framing-0005\r\n
169 s> content-type: application/mercurial-exp-framing-0005\r\n
170 s> content-length: 104\r\n
171 s> host: $LOCALIP:$HGPORT\r\n (glob)
172 s> user-agent: Mercurial debugwireproto\r\n
173 s> \r\n
174 s> `\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa3Ffields\x81GparentsEnodes\x82T\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&AT\xa9\x88\xfbCX>\x87\x1d\x1e\xd5u\x0e\xe0t\xc6\xd8@\xbb\xbf\xc8Dtree@DnameLmanifestdata
175 s> makefile('rb', None)
176 s> HTTP/1.1 200 OK\r\n
177 s> Server: testing stub value\r\n
178 s> Date: $HTTP_DATE$\r\n
179 s> Content-Type: application/mercurial-exp-framing-0005\r\n
180 s> Transfer-Encoding: chunked\r\n
181 s> \r\n
182 s> 13\r\n
183 s> \x0b\x00\x00\x01\x00\x02\x011
184 s> \xa1FstatusBok
185 s> \r\n
186 received frame(size=11; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=continuation)
187 s> b1\r\n
188 s> \xa9\x00\x00\x01\x00\x02\x001
189 s> \xa1Jtotalitems\x02\xa2DnodeT\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&AGparents\x82T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa2DnodeT\xa9\x88\xfbCX>\x87\x1d\x1e\xd5u\x0e\xe0t\xc6\xd8@\xbb\xbf\xc8Gparents\x82T\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&AT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
190 s> \r\n
191 received frame(size=169; request=1; stream=2; streamflags=; type=command-response; flags=continuation)
192 s> 8\r\n
193 s> \x00\x00\x00\x01\x00\x02\x002
194 s> \r\n
195 s> 0\r\n
196 s> \r\n
197 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
198 response: gen[
199 {
200 b'totalitems': 2
201 },
202 {
203 b'node': b'\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
204 b'parents': [
205 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
206 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
207 ]
208 },
209 {
210 b'node': b'\xa9\x88\xfbCX>\x87\x1d\x1e\xd5u\x0e\xe0t\xc6\xd8@\xbb\xbf\xc8',
211 b'parents': [
212 b'\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
213 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
214 ]
215 }
216 ]
217
218 $ cat .hg/blackbox.log
219 *> cacher constructed for manifestdata (glob)
220 *> cache miss for c045a581599d58608efd3d93d8129841f2af04a0 (glob)
221 *> storing cache entry for c045a581599d58608efd3d93d8129841f2af04a0 (glob)
222 *> cacher constructed for manifestdata (glob)
223 *> cache hit for c045a581599d58608efd3d93d8129841f2af04a0 (glob)
224 *> cacher constructed for manifestdata (glob)
225 *> cache miss for 6ed2f740a1cdd12c9e99c4f27695543143c26a11 (glob)
226 *> storing cache entry for 6ed2f740a1cdd12c9e99c4f27695543143c26a11 (glob)
227
228 $ cat error.log
229
230 $ killdaemons.py
231 $ rm .hg/blackbox.log
232
233 Try with object caching mode
234
235 $ cat >> .hg/hgrc << EOF
236 > [simplecache]
237 > cacheobjects = true
238 > EOF
239
240 $ hg serve -p $HGPORT -d --pid-file hg.pid -E error.log
241 $ cat hg.pid > $DAEMON_PIDS
242
243 $ sendhttpv2peer << EOF
244 > command manifestdata
245 > nodes eval:[b'\x99\x2f\x47\x79\x02\x9a\x3d\xf8\xd0\x66\x6d\x00\xbb\x92\x4f\x69\x63\x4e\x26\x41']
246 > tree eval:b''
247 > fields eval:[b'parents']
248 > EOF
249 creating http peer for wire protocol version 2
250 sending manifestdata command
251 s> POST /api/exp-http-v2-0002/ro/manifestdata HTTP/1.1\r\n
252 s> Accept-Encoding: identity\r\n
253 s> accept: application/mercurial-exp-framing-0005\r\n
254 s> content-type: application/mercurial-exp-framing-0005\r\n
255 s> content-length: 83\r\n
256 s> host: $LOCALIP:$HGPORT\r\n (glob)
257 s> user-agent: Mercurial debugwireproto\r\n
258 s> \r\n
259 s> K\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa3Ffields\x81GparentsEnodes\x81T\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&ADtree@DnameLmanifestdata
260 s> makefile('rb', None)
261 s> HTTP/1.1 200 OK\r\n
262 s> Server: testing stub value\r\n
263 s> Date: $HTTP_DATE$\r\n
264 s> Content-Type: application/mercurial-exp-framing-0005\r\n
265 s> Transfer-Encoding: chunked\r\n
266 s> \r\n
267 s> 13\r\n
268 s> \x0b\x00\x00\x01\x00\x02\x011
269 s> \xa1FstatusBok
270 s> \r\n
271 received frame(size=11; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=continuation)
272 s> 63\r\n
273 s> [\x00\x00\x01\x00\x02\x001
274 s> \xa1Jtotalitems\x01\xa2DnodeT\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&AGparents\x82T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
275 s> \r\n
276 received frame(size=91; request=1; stream=2; streamflags=; type=command-response; flags=continuation)
277 s> 8\r\n
278 s> \x00\x00\x00\x01\x00\x02\x002
279 s> \r\n
280 s> 0\r\n
281 s> \r\n
282 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
283 response: gen[
284 {
285 b'totalitems': 1
286 },
287 {
288 b'node': b'\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
289 b'parents': [
290 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
291 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
292 ]
293 }
294 ]
295
296 $ sendhttpv2peer << EOF
297 > command manifestdata
298 > nodes eval:[b'\x99\x2f\x47\x79\x02\x9a\x3d\xf8\xd0\x66\x6d\x00\xbb\x92\x4f\x69\x63\x4e\x26\x41']
299 > tree eval:b''
300 > fields eval:[b'parents']
301 > EOF
302 creating http peer for wire protocol version 2
303 sending manifestdata command
304 s> POST /api/exp-http-v2-0002/ro/manifestdata HTTP/1.1\r\n
305 s> Accept-Encoding: identity\r\n
306 s> accept: application/mercurial-exp-framing-0005\r\n
307 s> content-type: application/mercurial-exp-framing-0005\r\n
308 s> content-length: 83\r\n
309 s> host: $LOCALIP:$HGPORT\r\n (glob)
310 s> user-agent: Mercurial debugwireproto\r\n
311 s> \r\n
312 s> K\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa3Ffields\x81GparentsEnodes\x81T\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&ADtree@DnameLmanifestdata
313 s> makefile('rb', None)
314 s> HTTP/1.1 200 OK\r\n
315 s> Server: testing stub value\r\n
316 s> Date: $HTTP_DATE$\r\n
317 s> Content-Type: application/mercurial-exp-framing-0005\r\n
318 s> Transfer-Encoding: chunked\r\n
319 s> \r\n
320 s> 13\r\n
321 s> \x0b\x00\x00\x01\x00\x02\x011
322 s> \xa1FstatusBok
323 s> \r\n
324 received frame(size=11; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=continuation)
325 s> 63\r\n
326 s> [\x00\x00\x01\x00\x02\x001
327 s> \xa1Jtotalitems\x01\xa2DnodeT\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&AGparents\x82T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
328 s> \r\n
329 received frame(size=91; request=1; stream=2; streamflags=; type=command-response; flags=continuation)
330 s> 8\r\n
331 s> \x00\x00\x00\x01\x00\x02\x002
332 s> \r\n
333 s> 0\r\n
334 s> \r\n
335 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
336 response: gen[
337 {
338 b'totalitems': 1
339 },
340 {
341 b'node': b'\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
342 b'parents': [
343 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
344 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
345 ]
346 }
347 ]
348
349 $ cat .hg/blackbox.log
350 *> cacher constructed for manifestdata (glob)
351 *> cache miss for c045a581599d58608efd3d93d8129841f2af04a0 (glob)
352 *> storing cache entry for c045a581599d58608efd3d93d8129841f2af04a0 (glob)
353 *> cacher constructed for manifestdata (glob)
354 *> cache hit for c045a581599d58608efd3d93d8129841f2af04a0 (glob)
355
356 $ cat error.log
357
358 $ killdaemons.py
359 $ rm .hg/blackbox.log
360
361 A non-cacheable command does not instantiate cacher
362
363 $ hg serve -p $HGPORT -d --pid-file hg.pid -E error.log
364 $ cat hg.pid > $DAEMON_PIDS
365 $ sendhttpv2peer << EOF
366 > command capabilities
367 > EOF
368 creating http peer for wire protocol version 2
369 sending capabilities command
370 s> POST /api/exp-http-v2-0002/ro/capabilities HTTP/1.1\r\n
371 s> Accept-Encoding: identity\r\n
372 s> accept: application/mercurial-exp-framing-0005\r\n
373 s> content-type: application/mercurial-exp-framing-0005\r\n
374 s> content-length: 27\r\n
375 s> host: $LOCALIP:$HGPORT\r\n (glob)
376 s> user-agent: Mercurial debugwireproto\r\n
377 s> \r\n
378 s> \x13\x00\x00\x01\x00\x01\x01\x11\xa1DnameLcapabilities
379 s> makefile('rb', None)
380 s> HTTP/1.1 200 OK\r\n
381 s> Server: testing stub value\r\n
382 s> Date: $HTTP_DATE$\r\n
383 s> Content-Type: application/mercurial-exp-framing-0005\r\n
384 s> Transfer-Encoding: chunked\r\n
385 s> \r\n
386 s> 13\r\n
387 s> \x0b\x00\x00\x01\x00\x02\x011
388 s> \xa1FstatusBok
389 s> \r\n
390 received frame(size=11; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=continuation)
391 s> 52b\r\n
392 s> #\x05\x00\x01\x00\x02\x001
393 s> \xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushKcompression\x82\xa1DnameDzstd\xa1DnameDzlibQframingmediatypes\x81X&application/mercurial-exp-framing-0005Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1
394 s> \r\n
395 received frame(size=1315; request=1; stream=2; streamflags=; type=command-response; flags=continuation)
396 s> 8\r\n
397 s> \x00\x00\x00\x01\x00\x02\x002
398 s> \r\n
399 s> 0\r\n
400 s> \r\n
401 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
402 response: gen[
403 {
404 b'commands': {
405 b'branchmap': {
406 b'args': {},
407 b'permissions': [
408 b'pull'
409 ]
410 },
411 b'capabilities': {
412 b'args': {},
413 b'permissions': [
414 b'pull'
415 ]
416 },
417 b'changesetdata': {
418 b'args': {
419 b'fields': {
420 b'default': set([]),
421 b'required': False,
422 b'type': b'set',
423 b'validvalues': set([
424 b'bookmarks',
425 b'parents',
426 b'phase',
427 b'revision'
428 ])
429 },
430 b'noderange': {
431 b'default': None,
432 b'required': False,
433 b'type': b'list'
434 },
435 b'nodes': {
436 b'default': None,
437 b'required': False,
438 b'type': b'list'
439 },
440 b'nodesdepth': {
441 b'default': None,
442 b'required': False,
443 b'type': b'int'
444 }
445 },
446 b'permissions': [
447 b'pull'
448 ]
449 },
450 b'filedata': {
451 b'args': {
452 b'fields': {
453 b'default': set([]),
454 b'required': False,
455 b'type': b'set',
456 b'validvalues': set([
457 b'parents',
458 b'revision'
459 ])
460 },
461 b'haveparents': {
462 b'default': False,
463 b'required': False,
464 b'type': b'bool'
465 },
466 b'nodes': {
467 b'required': True,
468 b'type': b'list'
469 },
470 b'path': {
471 b'required': True,
472 b'type': b'bytes'
473 }
474 },
475 b'permissions': [
476 b'pull'
477 ]
478 },
479 b'heads': {
480 b'args': {
481 b'publiconly': {
482 b'default': False,
483 b'required': False,
484 b'type': b'bool'
485 }
486 },
487 b'permissions': [
488 b'pull'
489 ]
490 },
491 b'known': {
492 b'args': {
493 b'nodes': {
494 b'default': [],
495 b'required': False,
496 b'type': b'list'
497 }
498 },
499 b'permissions': [
500 b'pull'
501 ]
502 },
503 b'listkeys': {
504 b'args': {
505 b'namespace': {
506 b'required': True,
507 b'type': b'bytes'
508 }
509 },
510 b'permissions': [
511 b'pull'
512 ]
513 },
514 b'lookup': {
515 b'args': {
516 b'key': {
517 b'required': True,
518 b'type': b'bytes'
519 }
520 },
521 b'permissions': [
522 b'pull'
523 ]
524 },
525 b'manifestdata': {
526 b'args': {
527 b'fields': {
528 b'default': set([]),
529 b'required': False,
530 b'type': b'set',
531 b'validvalues': set([
532 b'parents',
533 b'revision'
534 ])
535 },
536 b'haveparents': {
537 b'default': False,
538 b'required': False,
539 b'type': b'bool'
540 },
541 b'nodes': {
542 b'required': True,
543 b'type': b'list'
544 },
545 b'tree': {
546 b'required': True,
547 b'type': b'bytes'
548 }
549 },
550 b'permissions': [
551 b'pull'
552 ]
553 },
554 b'pushkey': {
555 b'args': {
556 b'key': {
557 b'required': True,
558 b'type': b'bytes'
559 },
560 b'namespace': {
561 b'required': True,
562 b'type': b'bytes'
563 },
564 b'new': {
565 b'required': True,
566 b'type': b'bytes'
567 },
568 b'old': {
569 b'required': True,
570 b'type': b'bytes'
571 }
572 },
573 b'permissions': [
574 b'push'
575 ]
576 }
577 },
578 b'compression': [
579 {
580 b'name': b'zstd'
581 },
582 {
583 b'name': b'zlib'
584 }
585 ],
586 b'framingmediatypes': [
587 b'application/mercurial-exp-framing-0005'
588 ],
589 b'pathfilterprefixes': set([
590 b'path:',
591 b'rootfilesin:'
592 ]),
593 b'rawrepoformats': [
594 b'generaldelta',
595 b'revlogv1'
596 ]
597 }
598 ]
599
600 $ test -f .hg/blackbox.log
601 [1]
602
603 An error is not cached
604
605 $ sendhttpv2peer << EOF
606 > command manifestdata
607 > nodes eval:[b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa']
608 > tree eval:b''
609 > fields eval:[b'parents']
610 > EOF
611 creating http peer for wire protocol version 2
612 sending manifestdata command
613 s> POST /api/exp-http-v2-0002/ro/manifestdata HTTP/1.1\r\n
614 s> Accept-Encoding: identity\r\n
615 s> accept: application/mercurial-exp-framing-0005\r\n
616 s> content-type: application/mercurial-exp-framing-0005\r\n
617 s> content-length: 83\r\n
618 s> host: $LOCALIP:$HGPORT\r\n (glob)
619 s> user-agent: Mercurial debugwireproto\r\n
620 s> \r\n
621 s> K\x00\x00\x01\x00\x01\x01\x11\xa2Dargs\xa3Ffields\x81GparentsEnodes\x81T\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaaDtree@DnameLmanifestdata
622 s> makefile('rb', None)
623 s> HTTP/1.1 200 OK\r\n
624 s> Server: testing stub value\r\n
625 s> Date: $HTTP_DATE$\r\n
626 s> Content-Type: application/mercurial-exp-framing-0005\r\n
627 s> Transfer-Encoding: chunked\r\n
628 s> \r\n
629 s> 51\r\n
630 s> I\x00\x00\x01\x00\x02\x012
631 s> \xa2Eerror\xa2Dargs\x81T\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaaGmessagePunknown node: %sFstatusEerror
632 s> \r\n
633 received frame(size=73; request=1; stream=2; streamflags=stream-begin; type=command-response; flags=eos)
634 s> 0\r\n
635 s> \r\n
636 abort: unknown node: \xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa! (esc)
637 [255]
638
639 $ cat .hg/blackbox.log
640 *> cacher constructed for manifestdata (glob)
641 *> cache miss for 9d1bb421d99e913d45f2d099aa49728514292dd2 (glob)
642 *> cacher exiting due to error (glob)
643
644 $ killdaemons.py
645 $ rm .hg/blackbox.log
@@ -0,0 +1,100 b''
1 # wireprotosimplecache.py - Extension providing in-memory wire protocol cache
2 #
3 # Copyright 2018 Gregory Szorc <gregory.szorc@gmail.com>
4 #
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
7
8 from __future__ import absolute_import
9
10 from mercurial import (
11 extensions,
12 registrar,
13 repository,
14 util,
15 wireprototypes,
16 wireprotov2server,
17 )
18 from mercurial.utils import (
19 interfaceutil,
20 )
21
22 CACHE = None
23
24 configtable = {}
25 configitem = registrar.configitem(configtable)
26
27 configitem('simplecache', 'cacheobjects',
28 default=False)
29
30 @interfaceutil.implementer(repository.iwireprotocolcommandcacher)
31 class memorycacher(object):
32 def __init__(self, ui, command, encodefn):
33 self.ui = ui
34 self.encodefn = encodefn
35 self.key = None
36 self.cacheobjects = ui.configbool('simplecache', 'cacheobjects')
37 self.buffered = []
38
39 ui.log('simplecache', 'cacher constructed for %s\n', command)
40
41 def __enter__(self):
42 return self
43
44 def __exit__(self, exctype, excvalue, exctb):
45 if exctype:
46 self.ui.log('simplecache', 'cacher exiting due to error\n')
47
48 def adjustcachekeystate(self, state):
49 # Needed in order to make tests deterministic. Don't copy this
50 # pattern for production caches!
51 del state[b'repo']
52
53 def setcachekey(self, key):
54 self.key = key
55 return True
56
57 def lookup(self):
58 if self.key not in CACHE:
59 self.ui.log('simplecache', 'cache miss for %s\n', self.key)
60 return None
61
62 entry = CACHE[self.key]
63 self.ui.log('simplecache', 'cache hit for %s\n', self.key)
64
65 if self.cacheobjects:
66 return {
67 'objs': entry,
68 }
69 else:
70 return {
71 'objs': [wireprototypes.encodedresponse(entry)],
72 }
73
74 def onobject(self, obj):
75 if self.cacheobjects:
76 self.buffered.append(obj)
77 else:
78 self.buffered.extend(self.encodefn(obj))
79
80 yield obj
81
82 def onfinished(self):
83 self.ui.log('simplecache', 'storing cache entry for %s\n', self.key)
84 if self.cacheobjects:
85 CACHE[self.key] = self.buffered
86 else:
87 CACHE[self.key] = b''.join(self.buffered)
88
89 return []
90
91 def makeresponsecacher(orig, repo, proto, command, args, objencoderfn):
92 return memorycacher(repo.ui, command, objencoderfn)
93
94 def extsetup(ui):
95 global CACHE
96
97 CACHE = util.lrucachedict(10000)
98
99 extensions.wrapfunction(wireprotov2server, 'makeresponsecacher',
100 makeresponsecacher)
@@ -1657,3 +1657,160 b' class ilocalrepositorymain(interfaceutil'
1657 class completelocalrepository(ilocalrepositorymain,
1657 class completelocalrepository(ilocalrepositorymain,
1658 ilocalrepositoryfilestorage):
1658 ilocalrepositoryfilestorage):
1659 """Complete interface for a local repository."""
1659 """Complete interface for a local repository."""
1660
1661 class iwireprotocolcommandcacher(interfaceutil.Interface):
1662 """Represents a caching backend for wire protocol commands.
1663
1664 Wire protocol version 2 supports transparent caching of many commands.
1665 To leverage this caching, servers can activate objects that cache
1666 command responses. Objects handle both cache writing and reading.
1667 This interface defines how that response caching mechanism works.
1668
1669 Wire protocol version 2 commands emit a series of objects that are
1670 serialized and sent to the client. The caching layer exists between
1671 the invocation of the command function and the sending of its output
1672 objects to an output layer.
1673
1674 Instances of this interface represent a binding to a cache that
1675 can serve a response (in place of calling a command function) and/or
1676 write responses to a cache for subsequent use.
1677
1678 When a command request arrives, the following happens with regards
1679 to this interface:
1680
1681 1. The server determines whether the command request is cacheable.
1682 2. If it is, an instance of this interface is spawned.
1683 3. The cacher is activated in a context manager (``__enter__`` is called).
1684 4. A cache *key* for that request is derived. This will call the
1685 instance's ``adjustcachekeystate()`` method so the derivation
1686 can be influenced.
1687 5. The cacher is informed of the derived cache key via a call to
1688 ``setcachekey()``.
1689 6. The cacher's ``lookup()`` method is called to test for presence of
1690 the derived key in the cache.
1691 7. If ``lookup()`` returns a hit, that cached result is used in place
1692 of invoking the command function. ``__exit__`` is called and the instance
1693 is discarded.
1694 8. The command function is invoked.
1695 9. ``onobject()`` is called for each object emitted by the command
1696 function.
1697 10. After the final object is seen, ``onoutputfinished()`` is called.
1698 11. ``__exit__`` is called to signal the end of use of the instance.
1699
1700 Cache *key* derivation can be influenced by the instance.
1701
1702 Cache keys are initially derived by a deterministic representation of
1703 the command request. This includes the command name, arguments, protocol
1704 version, etc. This initial key derivation is performed by CBOR-encoding a
1705 data structure and feeding that output into a hasher.
1706
1707 Instances of this interface can influence this initial key derivation
1708 via ``adjustcachekeystate()``.
1709
1710 The instance is informed of the derived cache key via a call to
1711 ``setcachekey()``. The instance must store the key locally so it can
1712 be consulted on subsequent operations that may require it.
1713
1714 When constructed, the instance has access to a callable that can be used
1715 for encoding response objects. This callable receives as its single
1716 argument an object emitted by a command function. It returns an iterable
1717 of bytes chunks representing the encoded object. Unless the cacher is
1718 caching native Python objects in memory or has a way of reconstructing
1719 the original Python objects, implementations typically call this function
1720 to produce bytes from the output objects and then store those bytes in
1721 the cache. When it comes time to re-emit those bytes, they are wrapped
1722 in a ``wireprototypes.encodedresponse`` instance to tell the output
1723 layer that they are pre-encoded.
1724
1725 When receiving the objects emitted by the command function, instances
1726 can choose what to do with those objects. The simplest thing to do is
1727 re-emit the original objects. They will be forwarded to the output
1728 layer and will be processed as if the cacher did not exist.
1729
1730 Implementations could also choose to not emit objects - instead locally
1731 buffering objects or their encoded representation. They could then emit
1732 a single "coalesced" object when ``onoutputfinished()`` is called. In
1733 this way, the implementation would function as a filtering layer of
1734 sorts.
1735
1736 When caching objects, typically the encoded form of the object will
1737 be stored. Keep in mind that if the original object is forwarded to
1738 the output layer, it will need to be encoded there as well. For large
1739 output, this redundant encoding could add overhead. Implementations
1740 could wrap the encoded object data in ``wireprototypes.encodedresponse``
1741 instances to avoid this overhead.
1742 """
1743 def __enter__():
1744 """Marks the instance as active.
1745
1746 Should return self.
1747 """
1748
1749 def __exit__(exctype, excvalue, exctb):
1750 """Called when cacher is no longer used.
1751
1752 This can be used by implementations to perform cleanup actions (e.g.
1753 disconnecting network sockets, aborting a partially cached response.
1754 """
1755
1756 def adjustcachekeystate(state):
1757 """Influences cache key derivation by adjusting state to derive key.
1758
1759 A dict defining the state used to derive the cache key is passed.
1760
1761 Implementations can modify this dict to record additional state that
1762 is wanted to influence key derivation.
1763
1764 Implementations are *highly* encouraged to not modify or delete
1765 existing keys.
1766 """
1767
1768 def setcachekey(key):
1769 """Record the derived cache key for this request.
1770
1771 Instances may mutate the key for internal usage, as desired. e.g.
1772 instances may wish to prepend the repo name, introduce path
1773 components for filesystem or URL addressing, etc. Behavior is up to
1774 the cache.
1775
1776 Returns a bool indicating if the request is cacheable by this
1777 instance.
1778 """
1779
1780 def lookup():
1781 """Attempt to resolve an entry in the cache.
1782
1783 The instance is instructed to look for the cache key that it was
1784 informed about via the call to ``setcachekey()``.
1785
1786 If there's no cache hit or the cacher doesn't wish to use the cached
1787 entry, ``None`` should be returned.
1788
1789 Else, a dict defining the cached result should be returned. The
1790 dict may have the following keys:
1791
1792 objs
1793 An iterable of objects that should be sent to the client. That
1794 iterable of objects is expected to be what the command function
1795 would return if invoked or an equivalent representation thereof.
1796 """
1797
1798 def onobject(obj):
1799 """Called when a new object is emitted from the command function.
1800
1801 Receives as its argument the object that was emitted from the
1802 command function.
1803
1804 This method returns an iterator of objects to forward to the output
1805 layer. The easiest implementation is a generator that just
1806 ``yield obj``.
1807 """
1808
1809 def onfinished():
1810 """Called after all objects have been emitted from the command function.
1811
1812 Implementations should return an iterator of objects to forward to
1813 the output layer.
1814
1815 This method can be a generator.
1816 """
@@ -232,11 +232,12 b' class baseprotocolhandler(interfaceutil.'
232 class commandentry(object):
232 class commandentry(object):
233 """Represents a declared wire protocol command."""
233 """Represents a declared wire protocol command."""
234 def __init__(self, func, args='', transports=None,
234 def __init__(self, func, args='', transports=None,
235 permission='push'):
235 permission='push', cachekeyfn=None):
236 self.func = func
236 self.func = func
237 self.args = args
237 self.args = args
238 self.transports = transports or set()
238 self.transports = transports or set()
239 self.permission = permission
239 self.permission = permission
240 self.cachekeyfn = cachekeyfn
240
241
241 def _merge(self, func, args):
242 def _merge(self, func, args):
242 """Merge this instance with an incoming 2-tuple.
243 """Merge this instance with an incoming 2-tuple.
@@ -7,6 +7,7 b''
7 from __future__ import absolute_import
7 from __future__ import absolute_import
8
8
9 import contextlib
9 import contextlib
10 import hashlib
10
11
11 from .i18n import _
12 from .i18n import _
12 from .node import (
13 from .node import (
@@ -25,6 +26,7 b' from . import ('
25 wireprototypes,
26 wireprototypes,
26 )
27 )
27 from .utils import (
28 from .utils import (
29 cborutil,
28 interfaceutil,
30 interfaceutil,
29 stringutil,
31 stringutil,
30 )
32 )
@@ -35,6 +37,11 b' HTTP_WIREPROTO_V2 = wireprototypes.HTTP_'
35
37
36 COMMANDS = wireprototypes.commanddict()
38 COMMANDS = wireprototypes.commanddict()
37
39
40 # Value inserted into cache key computation function. Change the value to
41 # force new cache keys for every command request. This should be done when
42 # there is a change to how caching works, etc.
43 GLOBAL_CACHE_VERSION = 1
44
38 def handlehttpv2request(rctx, req, res, checkperm, urlparts):
45 def handlehttpv2request(rctx, req, res, checkperm, urlparts):
39 from .hgweb import common as hgwebcommon
46 from .hgweb import common as hgwebcommon
40
47
@@ -333,12 +340,64 b' def getdispatchrepo(repo, proto, command'
333 return repo.filtered('served')
340 return repo.filtered('served')
334
341
335 def dispatch(repo, proto, command):
342 def dispatch(repo, proto, command):
343 """Run a wire protocol command.
344
345 Returns an iterable of objects that will be sent to the client.
346 """
336 repo = getdispatchrepo(repo, proto, command)
347 repo = getdispatchrepo(repo, proto, command)
337
348
338 func, spec = COMMANDS[command]
349 entry = COMMANDS[command]
350 func = entry.func
351 spec = entry.args
352
339 args = proto.getargs(spec)
353 args = proto.getargs(spec)
340
354
341 return func(repo, proto, **pycompat.strkwargs(args))
355 # There is some duplicate boilerplate code here for calling the command and
356 # emitting objects. It is either that or a lot of indented code that looks
357 # like a pyramid (since there are a lot of code paths that result in not
358 # using the cacher).
359 callcommand = lambda: func(repo, proto, **pycompat.strkwargs(args))
360
361 # Request is not cacheable. Don't bother instantiating a cacher.
362 if not entry.cachekeyfn:
363 for o in callcommand():
364 yield o
365 return
366
367 cacher = makeresponsecacher(repo, proto, command, args,
368 cborutil.streamencode)
369
370 # But we have no cacher. Do default handling.
371 if not cacher:
372 for o in callcommand():
373 yield o
374 return
375
376 with cacher:
377 cachekey = entry.cachekeyfn(repo, proto, cacher, **args)
378
379 # No cache key or the cacher doesn't like it. Do default handling.
380 if cachekey is None or not cacher.setcachekey(cachekey):
381 for o in callcommand():
382 yield o
383 return
384
385 # Serve it from the cache, if possible.
386 cached = cacher.lookup()
387
388 if cached:
389 for o in cached['objs']:
390 yield o
391 return
392
393 # Else call the command and feed its output into the cacher, allowing
394 # the cacher to buffer/mutate objects as it desires.
395 for o in callcommand():
396 for o in cacher.onobject(o):
397 yield o
398
399 for o in cacher.onfinished():
400 yield o
342
401
343 @interfaceutil.implementer(wireprototypes.baseprotocolhandler)
402 @interfaceutil.implementer(wireprototypes.baseprotocolhandler)
344 class httpv2protocolhandler(object):
403 class httpv2protocolhandler(object):
@@ -460,7 +519,7 b' def _capabilitiesv2(repo, proto):'
460
519
461 return proto.addcapabilities(repo, caps)
520 return proto.addcapabilities(repo, caps)
462
521
463 def wireprotocommand(name, args=None, permission='push'):
522 def wireprotocommand(name, args=None, permission='push', cachekeyfn=None):
464 """Decorator to declare a wire protocol command.
523 """Decorator to declare a wire protocol command.
465
524
466 ``name`` is the name of the wire protocol command being provided.
525 ``name`` is the name of the wire protocol command being provided.
@@ -489,11 +548,21 b' def wireprotocommand(name, args=None, pe'
489 because otherwise commands not declaring their permissions could modify
548 because otherwise commands not declaring their permissions could modify
490 a repository that is supposed to be read-only.
549 a repository that is supposed to be read-only.
491
550
551 ``cachekeyfn`` defines an optional callable that can derive the
552 cache key for this request.
553
492 Wire protocol commands are generators of objects to be serialized and
554 Wire protocol commands are generators of objects to be serialized and
493 sent to the client.
555 sent to the client.
494
556
495 If a command raises an uncaught exception, this will be translated into
557 If a command raises an uncaught exception, this will be translated into
496 a command error.
558 a command error.
559
560 All commands can opt in to being cacheable by defining a function
561 (``cachekeyfn``) that is called to derive a cache key. This function
562 receives the same arguments as the command itself plus a ``cacher``
563 argument containing the active cacher for the request and returns a bytes
564 containing the key in a cache the response to this command may be cached
565 under.
497 """
566 """
498 transports = {k for k, v in wireprototypes.TRANSPORTS.items()
567 transports = {k for k, v in wireprototypes.TRANSPORTS.items()
499 if v['version'] == 2}
568 if v['version'] == 2}
@@ -543,12 +612,97 b' def wireprotocommand(name, args=None, pe'
543 'for version 2' % name)
612 'for version 2' % name)
544
613
545 COMMANDS[name] = wireprototypes.commandentry(
614 COMMANDS[name] = wireprototypes.commandentry(
546 func, args=args, transports=transports, permission=permission)
615 func, args=args, transports=transports, permission=permission,
616 cachekeyfn=cachekeyfn)
547
617
548 return func
618 return func
549
619
550 return register
620 return register
551
621
622 def makecommandcachekeyfn(command, localversion=None, allargs=False):
623 """Construct a cache key derivation function with common features.
624
625 By default, the cache key is a hash of:
626
627 * The command name.
628 * A global cache version number.
629 * A local cache version number (passed via ``localversion``).
630 * All the arguments passed to the command.
631 * The media type used.
632 * Wire protocol version string.
633 * The repository path.
634 """
635 if not allargs:
636 raise error.ProgrammingError('only allargs=True is currently supported')
637
638 if localversion is None:
639 raise error.ProgrammingError('must set localversion argument value')
640
641 def cachekeyfn(repo, proto, cacher, **args):
642 spec = COMMANDS[command]
643
644 # Commands that mutate the repo can not be cached.
645 if spec.permission == 'push':
646 return None
647
648 # TODO config option to disable caching.
649
650 # Our key derivation strategy is to construct a data structure
651 # holding everything that could influence cacheability and to hash
652 # the CBOR representation of that. Using CBOR seems like it might
653 # be overkill. However, simpler hashing mechanisms are prone to
654 # duplicate input issues. e.g. if you just concatenate two values,
655 # "foo"+"bar" is identical to "fo"+"obar". Using CBOR provides
656 # "padding" between values and prevents these problems.
657
658 # Seed the hash with various data.
659 state = {
660 # To invalidate all cache keys.
661 b'globalversion': GLOBAL_CACHE_VERSION,
662 # More granular cache key invalidation.
663 b'localversion': localversion,
664 # Cache keys are segmented by command.
665 b'command': pycompat.sysbytes(command),
666 # Throw in the media type and API version strings so changes
667 # to exchange semantics invalid cache.
668 b'mediatype': FRAMINGTYPE,
669 b'version': HTTP_WIREPROTO_V2,
670 # So same requests for different repos don't share cache keys.
671 b'repo': repo.root,
672 }
673
674 # The arguments passed to us will have already been normalized.
675 # Default values will be set, etc. This is important because it
676 # means that it doesn't matter if clients send an explicit argument
677 # or rely on the default value: it will all normalize to the same
678 # set of arguments on the server and therefore the same cache key.
679 #
680 # Arguments by their very nature must support being encoded to CBOR.
681 # And the CBOR encoder is deterministic. So we hash the arguments
682 # by feeding the CBOR of their representation into the hasher.
683 if allargs:
684 state[b'args'] = pycompat.byteskwargs(args)
685
686 cacher.adjustcachekeystate(state)
687
688 hasher = hashlib.sha1()
689 for chunk in cborutil.streamencode(state):
690 hasher.update(chunk)
691
692 return pycompat.sysbytes(hasher.hexdigest())
693
694 return cachekeyfn
695
696 def makeresponsecacher(repo, proto, command, args, objencoderfn):
697 """Construct a cacher for a cacheable command.
698
699 Returns an ``iwireprotocolcommandcacher`` instance.
700
701 Extensions can monkeypatch this function to provide custom caching
702 backends.
703 """
704 return None
705
552 @wireprotocommand('branchmap', permission='pull')
706 @wireprotocommand('branchmap', permission='pull')
553 def branchmapv2(repo, proto):
707 def branchmapv2(repo, proto):
554 yield {encoding.fromlocal(k): v
708 yield {encoding.fromlocal(k): v
@@ -755,7 +909,11 b' def getfilestore(repo, proto, path):'
755 'example': b'foo.txt',
909 'example': b'foo.txt',
756 }
910 }
757 },
911 },
758 permission='pull')
912 permission='pull',
913 # TODO censoring a file revision won't invalidate the cache.
914 # Figure out a way to take censoring into account when deriving
915 # the cache key.
916 cachekeyfn=makecommandcachekeyfn('filedata', 1, allargs=True))
759 def filedata(repo, proto, haveparents, nodes, fields, path):
917 def filedata(repo, proto, haveparents, nodes, fields, path):
760 try:
918 try:
761 # Extensions may wish to access the protocol handler.
919 # Extensions may wish to access the protocol handler.
@@ -893,7 +1051,8 b' def lookupv2(repo, proto, key):'
893 'example': b'',
1051 'example': b'',
894 },
1052 },
895 },
1053 },
896 permission='pull')
1054 permission='pull',
1055 cachekeyfn=makecommandcachekeyfn('manifestdata', 1, allargs=True))
897 def manifestdata(repo, proto, haveparents, nodes, fields, tree):
1056 def manifestdata(repo, proto, haveparents, nodes, fields, tree):
898 store = repo.manifestlog.getstorage(tree)
1057 store = repo.manifestlog.getstorage(tree)
899
1058
General Comments 0
You need to be logged in to leave comments. Login now