sapling/remotefilelog/metadatastore.py
Durham Goode 12ec6ebcb4 store: force remote stores to always check the shared cache
Summary:
In a remotecontent/metadatastore a `get` request first runs prefetch, then reads
the resulting data from the shared cache store. Before this patch, the prefetch
would not download a value if it existed in the local data store, which means
nothing would be added to the shared cache store, and the `get` would fail. This
patch changes the remote stores to always prefetch based only on the contents of
the shared cache, so data will always be written.

This issue showed up when attempting to repack pack files that contained
references to nodes that were in the local store (which it didn't have access
to) but not the shared cache.

Test Plan:
Manually verified my issue disappeared. This isn't actually an issue
anymore, since future patches refactor the way repack works to not rely on the
remote stores, so this shouldn't be hit again. But it's a safe change
regardless.

Reviewers: #mercurial, ttung, mitrandir

Reviewed By: mitrandir

Differential Revision: https://phabricator.intern.facebook.com/D3278362

Signature: t1:3278362:1463086099:987d2fdd1c75e518f815c3159473e8cb22a15ba0
2016-05-16 10:59:09 -07:00

109 lines
3.5 KiB
Python

import os
import basestore, shallowutil
from mercurial import error, util
from mercurial.i18n import _
from mercurial.node import hex, nullid
class unionmetadatastore(object):
def __init__(self, *args, **kwargs):
self.stores = args
self.writestore = kwargs.get('writestore')
def getancestors(self, name, node):
"""Returns as many ancestors as we're aware of.
return value: {
node: (p1, p2, linknode, copyfrom),
...
}
"""
ancestors = {}
def traverse(curname, curnode):
# TODO: this algorithm has the potential to traverse parts of
# history twice. Ex: with A->B->C->F and A->B->D->F, both D and C
# may be queued as missing, then B and A are traversed for both.
queue = [(curname, curnode)]
missing = []
while queue:
name, node = queue.pop()
value = ancestors.get(node)
if not value:
missing.append((name, node))
continue
p1, p2, linknode, copyfrom = value
if p1 != nullid:
queue.append((copyfrom or curname, p1))
if p2 != nullid:
queue.append((curname, p2))
return missing
missing = [(name, node)]
while missing:
curname, curnode = missing.pop()
ancestors.update(self._getpartialancestors(curname, curnode))
newmissing = traverse(curname, curnode)
missing.extend(newmissing)
# TODO: ancestors should probably be (name, node) -> (value)
return ancestors
def _getpartialancestors(self, name, node):
for store in self.stores:
try:
return store.getancestors(name, node)
except KeyError:
pass
raise error.LookupError(node, name, _('no valid file history'))
def add(self, name, node, data):
raise RuntimeError("cannot add content only to remotefilelog "
"contentstore")
def getmissing(self, keys):
missing = keys
for store in self.stores:
if missing:
missing = store.getmissing(missing)
return missing
def markledger(self, ledger):
for store in self.stores:
store.markledger(ledger)
class remotefilelogmetadatastore(basestore.basestore):
def getancestors(self, name, node):
"""Returns as many ancestors as we're aware of.
return value: {
node: (p1, p2, linknode, copyfrom),
...
}
"""
data = self._getdata(name, node)
ancestors = shallowutil.ancestormap(data)
return ancestors
def add(self, name, node, parents, linknode):
raise RuntimeError("cannot add metadata only to remotefilelog "
"metadatastore")
class remotemetadatastore(object):
def __init__(self, ui, fileservice, shared):
self._fileservice = fileservice
self._shared = shared
def getancestors(self, name, node):
self._fileservice.prefetch([(name, hex(node))], force=True,
fetchdata=False, fetchhistory=True)
return self._shared.getancestors(name, node)
def add(self, name, node, data):
raise RuntimeError("cannot add to a remote store")
def getmissing(self, keys):
return keys
def markledger(self, ledger):
pass