comparison hgext/remotefilelog/metadatastore.py @ 40495:3a333a582d7b

remotefilelog: import pruned-down remotefilelog extension from hg-experimental This is remotefilelog as of my recent patches for compatibility with current tip of hg, minus support for old versions of Mercurial and some FB-specific features like their treemanifest extension and fetching linkrev data from a patched phabricator. The file extutil.py moved from hgext3rd to remotefilelog. This is not yet ready to be landed, consider it a preview for now. Planned changes include: * replace lz4 with zstd * rename some capabilities, requirements and wireproto commands to mark them as experimental * consolidate bits of shallowutil with related functions (eg readfile) I'm certainly open to other (small) changes, but my rough mission is to land this largely as-is so we can use it as a model of the functionality we need going forward for lazy-fetching of file contents from a server. # no-check-commit because of a few foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D4782
author Augie Fackler <augie@google.com>
date Thu, 27 Sep 2018 13:03:19 -0400
parents
children 13d4ad8d7801
comparison
equal deleted inserted replaced
40494:9aeb9e2d28a7 40495:3a333a582d7b
1 from __future__ import absolute_import
2
3 from mercurial.node import hex, nullid
4 from . import (
5 basestore,
6 shallowutil,
7 )
8
9 class unionmetadatastore(basestore.baseunionstore):
10 def __init__(self, *args, **kwargs):
11 super(unionmetadatastore, self).__init__(*args, **kwargs)
12
13 self.stores = args
14 self.writestore = kwargs.get('writestore')
15
16 # If allowincomplete==True then the union store can return partial
17 # ancestor lists, otherwise it will throw a KeyError if a full
18 # history can't be found.
19 self.allowincomplete = kwargs.get('allowincomplete', False)
20
21 def getancestors(self, name, node, known=None):
22 """Returns as many ancestors as we're aware of.
23
24 return value: {
25 node: (p1, p2, linknode, copyfrom),
26 ...
27 }
28 """
29 if known is None:
30 known = set()
31 if node in known:
32 return []
33
34 ancestors = {}
35 def traverse(curname, curnode):
36 # TODO: this algorithm has the potential to traverse parts of
37 # history twice. Ex: with A->B->C->F and A->B->D->F, both D and C
38 # may be queued as missing, then B and A are traversed for both.
39 queue = [(curname, curnode)]
40 missing = []
41 seen = set()
42 while queue:
43 name, node = queue.pop()
44 if (name, node) in seen:
45 continue
46 seen.add((name, node))
47 value = ancestors.get(node)
48 if not value:
49 missing.append((name, node))
50 continue
51 p1, p2, linknode, copyfrom = value
52 if p1 != nullid and p1 not in known:
53 queue.append((copyfrom or curname, p1))
54 if p2 != nullid and p2 not in known:
55 queue.append((curname, p2))
56 return missing
57
58 missing = [(name, node)]
59 while missing:
60 curname, curnode = missing.pop()
61 try:
62 ancestors.update(self._getpartialancestors(curname, curnode,
63 known=known))
64 newmissing = traverse(curname, curnode)
65 missing.extend(newmissing)
66 except KeyError:
67 # If we allow incomplete histories, don't throw.
68 if not self.allowincomplete:
69 raise
70 # If the requested name+node doesn't exist, always throw.
71 if (curname, curnode) == (name, node):
72 raise
73
74 # TODO: ancestors should probably be (name, node) -> (value)
75 return ancestors
76
77 @basestore.baseunionstore.retriable
78 def _getpartialancestors(self, name, node, known=None):
79 for store in self.stores:
80 try:
81 return store.getancestors(name, node, known=known)
82 except KeyError:
83 pass
84
85 raise KeyError((name, hex(node)))
86
87 @basestore.baseunionstore.retriable
88 def getnodeinfo(self, name, node):
89 for store in self.stores:
90 try:
91 return store.getnodeinfo(name, node)
92 except KeyError:
93 pass
94
95 raise KeyError((name, hex(node)))
96
97 def add(self, name, node, data):
98 raise RuntimeError("cannot add content only to remotefilelog "
99 "contentstore")
100
101 def getmissing(self, keys):
102 missing = keys
103 for store in self.stores:
104 if missing:
105 missing = store.getmissing(missing)
106 return missing
107
108 def markledger(self, ledger, options=None):
109 for store in self.stores:
110 store.markledger(ledger, options)
111
112 def getmetrics(self):
113 metrics = [s.getmetrics() for s in self.stores]
114 return shallowutil.sumdicts(*metrics)
115
116 class remotefilelogmetadatastore(basestore.basestore):
117 def getancestors(self, name, node, known=None):
118 """Returns as many ancestors as we're aware of.
119
120 return value: {
121 node: (p1, p2, linknode, copyfrom),
122 ...
123 }
124 """
125 data = self._getdata(name, node)
126 ancestors = shallowutil.ancestormap(data)
127 return ancestors
128
129 def getnodeinfo(self, name, node):
130 return self.getancestors(name, node)[node]
131
132 def add(self, name, node, parents, linknode):
133 raise RuntimeError("cannot add metadata only to remotefilelog "
134 "metadatastore")
135
136 class remotemetadatastore(object):
137 def __init__(self, ui, fileservice, shared):
138 self._fileservice = fileservice
139 self._shared = shared
140
141 def getancestors(self, name, node, known=None):
142 self._fileservice.prefetch([(name, hex(node))], force=True,
143 fetchdata=False, fetchhistory=True)
144 return self._shared.getancestors(name, node, known=known)
145
146 def getnodeinfo(self, name, node):
147 return self.getancestors(name, node)[node]
148
149 def add(self, name, node, data):
150 raise RuntimeError("cannot add to a remote store")
151
152 def getmissing(self, keys):
153 return keys
154
155 def markledger(self, ledger, options=None):
156 pass