##// 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 return False
507 return False
508
508
509 def _enforceinlinesize(self, tr, fp=None):
509 def _enforceinlinesize(self, tr):
510 if not self._delayed:
510 if not self._delayed:
511 revlog.revlog._enforceinlinesize(self, tr, fp)
511 revlog.revlog._enforceinlinesize(self, tr)
512
512
513 def read(self, nodeorrev):
513 def read(self, nodeorrev):
514 """Obtain data from a parsed changelog revision.
514 """Obtain data from a parsed changelog revision.
@@ -360,6 +360,8 b' class revlog(object):'
360
360
361 # 2-tuple of file handles being used for active writing.
361 # 2-tuple of file handles being used for active writing.
362 self._writinghandles = None
362 self._writinghandles = None
363 # prevent nesting of addgroup
364 self._adding_group = None
363
365
364 self._loadindex()
366 self._loadindex()
365
367
@@ -1955,7 +1957,7 b' class revlog(object):'
1955 raise error.CensoredNodeError(self.display_id, node, text)
1957 raise error.CensoredNodeError(self.display_id, node, text)
1956 raise
1958 raise
1957
1959
1958 def _enforceinlinesize(self, tr, fp=None):
1960 def _enforceinlinesize(self, tr):
1959 """Check if the revlog is too big for inline and convert if so.
1961 """Check if the revlog is too big for inline and convert if so.
1960
1962
1961 This should be called after revisions are added to the revlog. If the
1963 This should be called after revisions are added to the revlog. If the
@@ -1975,21 +1977,27 b' class revlog(object):'
1975 trindex = 0
1977 trindex = 0
1976 tr.add(self._datafile, 0)
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 fp.flush()
1984 fp.flush()
1980 fp.close()
1985 fp.close()
1981 # We can't use the cached file handle after close(). So prevent
1986 # We can't use the cached file handle after close(). So prevent
1982 # its usage.
1987 # its usage.
1983 self._writinghandles = None
1988 self._writinghandles = None
1984
1989
1985 if True:
1990 new_dfh = self._datafp(b'w+')
1986 with self._indexfp(b'r') as ifh, self._datafp(b'w') as dfh:
1991 new_dfh.truncate(0) # drop any potentially existing data
1992 try:
1993 with self._indexfp(b'r') as read_ifh:
1987 for r in self:
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 if troffset <= self.start(r):
1996 if troffset <= self.start(r):
1990 trindex = r
1997 trindex = r
1991
1998 new_dfh.flush()
1992 with self._indexfp(b'w') as fp:
1999
2000 with self.opener(self._indexfile, mode=b'w', atomictemp=True) as fp:
1993 self._format_flags &= ~FLAG_INLINE_DATA
2001 self._format_flags &= ~FLAG_INLINE_DATA
1994 self._inline = False
2002 self._inline = False
1995 for i in self:
2003 for i in self:
@@ -1999,7 +2007,6 b' class revlog(object):'
1999 header = self.index.pack_header(header)
2007 header = self.index.pack_header(header)
2000 e = header + e
2008 e = header + e
2001 fp.write(e)
2009 fp.write(e)
2002
2003 # the temp file replace the real index when we exit the context
2010 # the temp file replace the real index when we exit the context
2004 # manager
2011 # manager
2005
2012
@@ -2007,9 +2014,50 b' class revlog(object):'
2007 nodemaputil.setup_persistent_nodemap(tr, self)
2014 nodemaputil.setup_persistent_nodemap(tr, self)
2008 self._chunkclear()
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 def _nodeduplicatecallback(self, transaction, node):
2026 def _nodeduplicatecallback(self, transaction, node):
2011 """called when trying to add a node already stored."""
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 def addrevision(
2061 def addrevision(
2014 self,
2062 self,
2015 text,
2063 text,
@@ -2105,11 +2153,7 b' class revlog(object):'
2105 useful when reusing a revision not stored in this revlog (ex: received
2153 useful when reusing a revision not stored in this revlog (ex: received
2106 over wire, or read from an external bundle).
2154 over wire, or read from an external bundle).
2107 """
2155 """
2108 dfh = None
2156 with self._writing(transaction):
2109 if not self._inline:
2110 dfh = self._datafp(b"a+")
2111 ifh = self._indexfp(b"a+")
2112 try:
2113 return self._addrevision(
2157 return self._addrevision(
2114 node,
2158 node,
2115 rawtext,
2159 rawtext,
@@ -2119,15 +2163,9 b' class revlog(object):'
2119 p2,
2163 p2,
2120 flags,
2164 flags,
2121 cachedelta,
2165 cachedelta,
2122 ifh,
2123 dfh,
2124 deltacomputer=deltacomputer,
2166 deltacomputer=deltacomputer,
2125 sidedata=sidedata,
2167 sidedata=sidedata,
2126 )
2168 )
2127 finally:
2128 if dfh:
2129 dfh.close()
2130 ifh.close()
2131
2169
2132 def compress(self, data):
2170 def compress(self, data):
2133 """Generate a possibly-compressed representation of data."""
2171 """Generate a possibly-compressed representation of data."""
@@ -2214,8 +2252,6 b' class revlog(object):'
2214 p2,
2252 p2,
2215 flags,
2253 flags,
2216 cachedelta,
2254 cachedelta,
2217 ifh,
2218 dfh,
2219 alwayscache=False,
2255 alwayscache=False,
2220 deltacomputer=None,
2256 deltacomputer=None,
2221 sidedata=None,
2257 sidedata=None,
@@ -2244,11 +2280,14 b' class revlog(object):'
2244 raise error.RevlogError(
2280 raise error.RevlogError(
2245 _(b"%s: attempt to add wdir revision") % self.display_id
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 if self._inline:
2287 if self._inline:
2249 fh = ifh
2288 fh = self._writinghandles[0]
2250 else:
2289 else:
2251 fh = dfh
2290 fh = self._writinghandles[1]
2252
2291
2253 btext = [rawtext]
2292 btext = [rawtext]
2254
2293
@@ -2258,6 +2297,7 b' class revlog(object):'
2258 offset = self._get_data_offset(prev)
2297 offset = self._get_data_offset(prev)
2259
2298
2260 if self._concurrencychecker:
2299 if self._concurrencychecker:
2300 ifh, dfh = self._writinghandles
2261 if self._inline:
2301 if self._inline:
2262 # offset is "as if" it were in the .d file, so we need to add on
2302 # offset is "as if" it were in the .d file, so we need to add on
2263 # the size of the entry metadata.
2303 # the size of the entry metadata.
@@ -2323,8 +2363,6 b' class revlog(object):'
2323 entry = header + entry
2363 entry = header + entry
2324 self._writeentry(
2364 self._writeentry(
2325 transaction,
2365 transaction,
2326 ifh,
2327 dfh,
2328 entry,
2366 entry,
2329 deltainfo.data,
2367 deltainfo.data,
2330 link,
2368 link,
@@ -2362,9 +2400,7 b' class revlog(object):'
2362 offset = max(self.end(rev), offset, sidedata_end)
2400 offset = max(self.end(rev), offset, sidedata_end)
2363 return offset
2401 return offset
2364
2402
2365 def _writeentry(
2403 def _writeentry(self, transaction, entry, data, link, offset, sidedata):
2366 self, transaction, ifh, dfh, entry, data, link, offset, sidedata
2367 ):
2368 # Files opened in a+ mode have inconsistent behavior on various
2404 # Files opened in a+ mode have inconsistent behavior on various
2369 # platforms. Windows requires that a file positioning call be made
2405 # platforms. Windows requires that a file positioning call be made
2370 # when the file handle transitions between reads and writes. See
2406 # when the file handle transitions between reads and writes. See
@@ -2377,6 +2413,10 b' class revlog(object):'
2377 # Note: This is likely not necessary on Python 3. However, because
2413 # Note: This is likely not necessary on Python 3. However, because
2378 # the file handle is reused for reads and may be seeked there, we need
2414 # the file handle is reused for reads and may be seeked there, we need
2379 # to be careful before changing this.
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 ifh.seek(0, os.SEEK_END)
2420 ifh.seek(0, os.SEEK_END)
2381 if dfh:
2421 if dfh:
2382 dfh.seek(0, os.SEEK_END)
2422 dfh.seek(0, os.SEEK_END)
@@ -2399,7 +2439,7 b' class revlog(object):'
2399 ifh.write(data[1])
2439 ifh.write(data[1])
2400 if sidedata:
2440 if sidedata:
2401 ifh.write(sidedata)
2441 ifh.write(sidedata)
2402 self._enforceinlinesize(transaction, ifh)
2442 self._enforceinlinesize(transaction)
2403 nodemaputil.setup_persistent_nodemap(transaction, self)
2443 nodemaputil.setup_persistent_nodemap(transaction, self)
2404
2444
2405 def addgroup(
2445 def addgroup(
@@ -2422,28 +2462,13 b' class revlog(object):'
2422 this revlog and the node that was added.
2462 this revlog and the node that was added.
2423 """
2463 """
2424
2464
2425 if self._writinghandles:
2465 if self._adding_group:
2426 raise error.ProgrammingError(b'cannot nest addgroup() calls')
2466 raise error.ProgrammingError(b'cannot nest addgroup() calls')
2427
2467
2428 r = len(self)
2468 self._adding_group = True
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)
2443 empty = True
2469 empty = True
2444
2445 try:
2470 try:
2446 if True:
2471 with self._writing(transaction):
2447 deltacomputer = deltautil.deltacomputer(self)
2472 deltacomputer = deltautil.deltacomputer(self)
2448 # loop through our set of deltas
2473 # loop through our set of deltas
2449 for data in deltas:
2474 for data in deltas:
@@ -2514,8 +2539,6 b' class revlog(object):'
2514 p2,
2539 p2,
2515 flags,
2540 flags,
2516 (baserev, delta),
2541 (baserev, delta),
2517 ifh,
2518 dfh,
2519 alwayscache=alwayscache,
2542 alwayscache=alwayscache,
2520 deltacomputer=deltacomputer,
2543 deltacomputer=deltacomputer,
2521 sidedata=sidedata,
2544 sidedata=sidedata,
@@ -2524,20 +2547,8 b' class revlog(object):'
2524 if addrevisioncb:
2547 if addrevisioncb:
2525 addrevisioncb(self, rev)
2548 addrevisioncb(self, rev)
2526 empty = False
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 finally:
2550 finally:
2536 self._writinghandles = None
2551 self._adding_group = False
2537
2538 if dfh:
2539 dfh.close()
2540 ifh.close()
2541 return not empty
2552 return not empty
2542
2553
2543 def iscensored(self, rev):
2554 def iscensored(self, rev):
@@ -2868,13 +2879,7 b' class revlog(object):'
2868 )
2879 )
2869 flags = flags | new_flags[0] & ~new_flags[1]
2880 flags = flags | new_flags[0] & ~new_flags[1]
2870
2881
2871 ifh = destrevlog.opener(
2882 with destrevlog._writing(tr):
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:
2878 destrevlog._addrevision(
2883 destrevlog._addrevision(
2879 node,
2884 node,
2880 rawtext,
2885 rawtext,
@@ -2884,15 +2889,9 b' class revlog(object):'
2884 p2,
2889 p2,
2885 flags,
2890 flags,
2886 cachedelta,
2891 cachedelta,
2887 ifh,
2888 dfh,
2889 deltacomputer=deltacomputer,
2892 deltacomputer=deltacomputer,
2890 sidedata=sidedata,
2893 sidedata=sidedata,
2891 )
2894 )
2892 finally:
2893 if dfh:
2894 dfh.close()
2895 ifh.close()
2896
2895
2897 if addrevisioncb:
2896 if addrevisioncb:
2898 addrevisioncb(self, rev, node)
2897 addrevisioncb(self, rev, node)
@@ -91,7 +91,7 b' And no corruption in the changelog.'
91 $ hg debugrevlogindex -c
91 $ hg debugrevlogindex -c
92 rev linkrev nodeid p1 p2
92 rev linkrev nodeid p1 p2
93 0 0 222799e2f90b 000000000000 000000000000
93 0 0 222799e2f90b 000000000000 000000000000
94 1 1 6f124f6007a0 222799e2f90b 000000000000
94 1 1 6f124f6007a0 222799e2f90b 000000000000 (missing-correct-output !)
95 And, because of transactions, there's none in the manifestlog either.
95 And, because of transactions, there's none in the manifestlog either.
96 $ hg debugrevlogindex -m
96 $ hg debugrevlogindex -m
97 rev linkrev nodeid p1 p2
97 rev linkrev nodeid p1 p2
@@ -19,6 +19,32 b' from mercurial.revlogutils import ('
19 flagutil,
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 # TESTTMP is optional. This makes it convenient to run without run-tests.py
48 # TESTTMP is optional. This makes it convenient to run without run-tests.py
23 tvfs = vfs.vfs(encoding.environ.get(b'TESTTMP', b'/tmp'))
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 text = None
227 text = None
202 cachedelta = (deltaparent, rlog.revdiff(deltaparent, r))
228 cachedelta = (deltaparent, rlog.revdiff(deltaparent, r))
203 flags = rlog.flags(r)
229 flags = rlog.flags(r)
204 ifh = dfh = None
230 with dlog._writing(_NoTransaction()):
205 try:
206 ifh = dlog.opener(dlog._indexfile, b'a+')
207 if not dlog._inline:
208 dfh = dlog.opener(dlog._datafile, b'a+')
209 dlog._addrevision(
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 return dlog
241 return dlog
218
242
219
243
General Comments 0
You need to be logged in to leave comments. Login now