##// END OF EJS Templates
revlog: introduce a mandatory `_writing` context to update revlog content...
marmoute -
r47988:906a7bca default
parent child Browse files
Show More
@@ -506,9 +506,9 b' class changelog(revlog.revlog):'
506 506
507 507 return False
508 508
509 def _enforceinlinesize(self, tr, fp=None):
509 def _enforceinlinesize(self, tr):
510 510 if not self._delayed:
511 revlog.revlog._enforceinlinesize(self, tr, fp)
511 revlog.revlog._enforceinlinesize(self, tr)
512 512
513 513 def read(self, nodeorrev):
514 514 """Obtain data from a parsed changelog revision.
@@ -360,6 +360,8 b' class revlog(object):'
360 360
361 361 # 2-tuple of file handles being used for active writing.
362 362 self._writinghandles = None
363 # prevent nesting of addgroup
364 self._adding_group = None
363 365
364 366 self._loadindex()
365 367
@@ -1955,7 +1957,7 b' class revlog(object):'
1955 1957 raise error.CensoredNodeError(self.display_id, node, text)
1956 1958 raise
1957 1959
1958 def _enforceinlinesize(self, tr, fp=None):
1960 def _enforceinlinesize(self, tr):
1959 1961 """Check if the revlog is too big for inline and convert if so.
1960 1962
1961 1963 This should be called after revisions are added to the revlog. If the
@@ -1975,21 +1977,27 b' class revlog(object):'
1975 1977 trindex = 0
1976 1978 tr.add(self._datafile, 0)
1977 1979
1978 if fp:
1980 existing_handles = False
1981 if self._writinghandles is not None:
1982 existing_handles = True
1983 fp = self._writinghandles[0]
1979 1984 fp.flush()
1980 1985 fp.close()
1981 1986 # We can't use the cached file handle after close(). So prevent
1982 1987 # its usage.
1983 1988 self._writinghandles = None
1984 1989
1985 if True:
1986 with self._indexfp(b'r') as ifh, self._datafp(b'w') as dfh:
1990 new_dfh = self._datafp(b'w+')
1991 new_dfh.truncate(0) # drop any potentially existing data
1992 try:
1993 with self._indexfp(b'r') as read_ifh:
1987 1994 for r in self:
1988 dfh.write(self._getsegmentforrevs(r, r, df=ifh)[1])
1995 new_dfh.write(self._getsegmentforrevs(r, r, df=read_ifh)[1])
1989 1996 if troffset <= self.start(r):
1990 1997 trindex = r
1991
1992 with self._indexfp(b'w') as fp:
1998 new_dfh.flush()
1999
2000 with self.opener(self._indexfile, mode=b'w', atomictemp=True) as fp:
1993 2001 self._format_flags &= ~FLAG_INLINE_DATA
1994 2002 self._inline = False
1995 2003 for i in self:
@@ -1999,7 +2007,6 b' class revlog(object):'
1999 2007 header = self.index.pack_header(header)
2000 2008 e = header + e
2001 2009 fp.write(e)
2002
2003 2010 # the temp file replace the real index when we exit the context
2004 2011 # manager
2005 2012
@@ -2007,9 +2014,50 b' class revlog(object):'
2007 2014 nodemaputil.setup_persistent_nodemap(tr, self)
2008 2015 self._chunkclear()
2009 2016
2017 if existing_handles:
2018 # switched from inline to conventional reopen the index
2019 ifh = self._indexfp(b"a+")
2020 self._writinghandles = (ifh, new_dfh)
2021 new_dfh = None
2022 finally:
2023 if new_dfh is not None:
2024 new_dfh.close()
2025
2010 2026 def _nodeduplicatecallback(self, transaction, node):
2011 2027 """called when trying to add a node already stored."""
2012 2028
2029 @contextlib.contextmanager
2030 def _writing(self, transaction):
2031 if self._writinghandles is not None:
2032 yield
2033 else:
2034 r = len(self)
2035 dsize = 0
2036 if r:
2037 dsize = self.end(r - 1)
2038 dfh = None
2039 if not self._inline:
2040 dfh = self._datafp(b"a+")
2041 transaction.add(self._datafile, dsize)
2042 try:
2043 isize = r * self.index.entry_size
2044 ifh = self._indexfp(b"a+")
2045 if self._inline:
2046 transaction.add(self._indexfile, dsize + isize)
2047 else:
2048 transaction.add(self._indexfile, isize)
2049 try:
2050 self._writinghandles = (ifh, dfh)
2051 try:
2052 yield
2053 finally:
2054 self._writinghandles = None
2055 finally:
2056 ifh.close()
2057 finally:
2058 if dfh is not None:
2059 dfh.close()
2060
2013 2061 def addrevision(
2014 2062 self,
2015 2063 text,
@@ -2105,11 +2153,7 b' class revlog(object):'
2105 2153 useful when reusing a revision not stored in this revlog (ex: received
2106 2154 over wire, or read from an external bundle).
2107 2155 """
2108 dfh = None
2109 if not self._inline:
2110 dfh = self._datafp(b"a+")
2111 ifh = self._indexfp(b"a+")
2112 try:
2156 with self._writing(transaction):
2113 2157 return self._addrevision(
2114 2158 node,
2115 2159 rawtext,
@@ -2119,15 +2163,9 b' class revlog(object):'
2119 2163 p2,
2120 2164 flags,
2121 2165 cachedelta,
2122 ifh,
2123 dfh,
2124 2166 deltacomputer=deltacomputer,
2125 2167 sidedata=sidedata,
2126 2168 )
2127 finally:
2128 if dfh:
2129 dfh.close()
2130 ifh.close()
2131 2169
2132 2170 def compress(self, data):
2133 2171 """Generate a possibly-compressed representation of data."""
@@ -2214,8 +2252,6 b' class revlog(object):'
2214 2252 p2,
2215 2253 flags,
2216 2254 cachedelta,
2217 ifh,
2218 dfh,
2219 2255 alwayscache=False,
2220 2256 deltacomputer=None,
2221 2257 sidedata=None,
@@ -2244,11 +2280,14 b' class revlog(object):'
2244 2280 raise error.RevlogError(
2245 2281 _(b"%s: attempt to add wdir revision") % self.display_id
2246 2282 )
2283 if self._writinghandles is None:
2284 msg = b'adding revision outside `revlog._writing` context'
2285 raise error.ProgrammingError(msg)
2247 2286
2248 2287 if self._inline:
2249 fh = ifh
2288 fh = self._writinghandles[0]
2250 2289 else:
2251 fh = dfh
2290 fh = self._writinghandles[1]
2252 2291
2253 2292 btext = [rawtext]
2254 2293
@@ -2258,6 +2297,7 b' class revlog(object):'
2258 2297 offset = self._get_data_offset(prev)
2259 2298
2260 2299 if self._concurrencychecker:
2300 ifh, dfh = self._writinghandles
2261 2301 if self._inline:
2262 2302 # offset is "as if" it were in the .d file, so we need to add on
2263 2303 # the size of the entry metadata.
@@ -2323,8 +2363,6 b' class revlog(object):'
2323 2363 entry = header + entry
2324 2364 self._writeentry(
2325 2365 transaction,
2326 ifh,
2327 dfh,
2328 2366 entry,
2329 2367 deltainfo.data,
2330 2368 link,
@@ -2362,9 +2400,7 b' class revlog(object):'
2362 2400 offset = max(self.end(rev), offset, sidedata_end)
2363 2401 return offset
2364 2402
2365 def _writeentry(
2366 self, transaction, ifh, dfh, entry, data, link, offset, sidedata
2367 ):
2403 def _writeentry(self, transaction, entry, data, link, offset, sidedata):
2368 2404 # Files opened in a+ mode have inconsistent behavior on various
2369 2405 # platforms. Windows requires that a file positioning call be made
2370 2406 # when the file handle transitions between reads and writes. See
@@ -2377,6 +2413,10 b' class revlog(object):'
2377 2413 # Note: This is likely not necessary on Python 3. However, because
2378 2414 # the file handle is reused for reads and may be seeked there, we need
2379 2415 # to be careful before changing this.
2416 if self._writinghandles is None:
2417 msg = b'adding revision outside `revlog._writing` context'
2418 raise error.ProgrammingError(msg)
2419 ifh, dfh = self._writinghandles
2380 2420 ifh.seek(0, os.SEEK_END)
2381 2421 if dfh:
2382 2422 dfh.seek(0, os.SEEK_END)
@@ -2399,7 +2439,7 b' class revlog(object):'
2399 2439 ifh.write(data[1])
2400 2440 if sidedata:
2401 2441 ifh.write(sidedata)
2402 self._enforceinlinesize(transaction, ifh)
2442 self._enforceinlinesize(transaction)
2403 2443 nodemaputil.setup_persistent_nodemap(transaction, self)
2404 2444
2405 2445 def addgroup(
@@ -2422,28 +2462,13 b' class revlog(object):'
2422 2462 this revlog and the node that was added.
2423 2463 """
2424 2464
2425 if self._writinghandles:
2465 if self._adding_group:
2426 2466 raise error.ProgrammingError(b'cannot nest addgroup() calls')
2427 2467
2428 r = len(self)
2429 end = 0
2430 if r:
2431 end = self.end(r - 1)
2432 ifh = self._indexfp(b"a+")
2433 isize = r * self.index.entry_size
2434 if self._inline:
2435 transaction.add(self._indexfile, end + isize)
2436 dfh = None
2437 else:
2438 transaction.add(self._indexfile, isize)
2439 transaction.add(self._datafile, end)
2440 dfh = self._datafp(b"a+")
2441
2442 self._writinghandles = (ifh, dfh)
2468 self._adding_group = True
2443 2469 empty = True
2444
2445 2470 try:
2446 if True:
2471 with self._writing(transaction):
2447 2472 deltacomputer = deltautil.deltacomputer(self)
2448 2473 # loop through our set of deltas
2449 2474 for data in deltas:
@@ -2514,8 +2539,6 b' class revlog(object):'
2514 2539 p2,
2515 2540 flags,
2516 2541 (baserev, delta),
2517 ifh,
2518 dfh,
2519 2542 alwayscache=alwayscache,
2520 2543 deltacomputer=deltacomputer,
2521 2544 sidedata=sidedata,
@@ -2524,20 +2547,8 b' class revlog(object):'
2524 2547 if addrevisioncb:
2525 2548 addrevisioncb(self, rev)
2526 2549 empty = False
2527
2528 if not dfh and not self._inline:
2529 # addrevision switched from inline to conventional
2530 # reopen the index
2531 ifh.close()
2532 dfh = self._datafp(b"a+")
2533 ifh = self._indexfp(b"a+")
2534 self._writinghandles = (ifh, dfh)
2535 2550 finally:
2536 self._writinghandles = None
2537
2538 if dfh:
2539 dfh.close()
2540 ifh.close()
2551 self._adding_group = False
2541 2552 return not empty
2542 2553
2543 2554 def iscensored(self, rev):
@@ -2868,13 +2879,7 b' class revlog(object):'
2868 2879 )
2869 2880 flags = flags | new_flags[0] & ~new_flags[1]
2870 2881
2871 ifh = destrevlog.opener(
2872 destrevlog._indexfile, b'a+', checkambig=False
2873 )
2874 dfh = None
2875 if not destrevlog._inline:
2876 dfh = destrevlog.opener(destrevlog._datafile, b'a+')
2877 try:
2882 with destrevlog._writing(tr):
2878 2883 destrevlog._addrevision(
2879 2884 node,
2880 2885 rawtext,
@@ -2884,15 +2889,9 b' class revlog(object):'
2884 2889 p2,
2885 2890 flags,
2886 2891 cachedelta,
2887 ifh,
2888 dfh,
2889 2892 deltacomputer=deltacomputer,
2890 2893 sidedata=sidedata,
2891 2894 )
2892 finally:
2893 if dfh:
2894 dfh.close()
2895 ifh.close()
2896 2895
2897 2896 if addrevisioncb:
2898 2897 addrevisioncb(self, rev, node)
@@ -91,7 +91,7 b' And no corruption in the changelog.'
91 91 $ hg debugrevlogindex -c
92 92 rev linkrev nodeid p1 p2
93 93 0 0 222799e2f90b 000000000000 000000000000
94 1 1 6f124f6007a0 222799e2f90b 000000000000
94 1 1 6f124f6007a0 222799e2f90b 000000000000 (missing-correct-output !)
95 95 And, because of transactions, there's none in the manifestlog either.
96 96 $ hg debugrevlogindex -m
97 97 rev linkrev nodeid p1 p2
@@ -19,6 +19,32 b' from mercurial.revlogutils import ('
19 19 flagutil,
20 20 )
21 21
22
23 class _NoTransaction(object):
24 """transaction like object to update the nodemap outside a transaction"""
25
26 def __init__(self):
27 self._postclose = {}
28
29 def addpostclose(self, callback_id, callback_func):
30 self._postclose[callback_id] = callback_func
31
32 def registertmp(self, *args, **kwargs):
33 pass
34
35 def addbackup(self, *args, **kwargs):
36 pass
37
38 def add(self, *args, **kwargs):
39 pass
40
41 def addabort(self, *args, **kwargs):
42 pass
43
44 def _report(self, *args):
45 pass
46
47
22 48 # TESTTMP is optional. This makes it convenient to run without run-tests.py
23 49 tvfs = vfs.vfs(encoding.environ.get(b'TESTTMP', b'/tmp'))
24 50
@@ -201,19 +227,17 b" def lowlevelcopy(rlog, tr, destname=b'_d"
201 227 text = None
202 228 cachedelta = (deltaparent, rlog.revdiff(deltaparent, r))
203 229 flags = rlog.flags(r)
204 ifh = dfh = None
205 try:
206 ifh = dlog.opener(dlog._indexfile, b'a+')
207 if not dlog._inline:
208 dfh = dlog.opener(dlog._datafile, b'a+')
230 with dlog._writing(_NoTransaction()):
209 231 dlog._addrevision(
210 rlog.node(r), text, tr, r, p1, p2, flags, cachedelta, ifh, dfh
232 rlog.node(r),
233 text,
234 tr,
235 r,
236 p1,
237 p2,
238 flags,
239 cachedelta,
211 240 )
212 finally:
213 if dfh is not None:
214 dfh.close()
215 if ifh is not None:
216 ifh.close()
217 241 return dlog
218 242
219 243
General Comments 0
You need to be logged in to leave comments. Login now