2010-05-14 00:49:17 +00:00
|
|
|
"""
|
|
|
|
I contain utilities useful for calculating servers_of_happiness, and for
|
|
|
|
reporting it in messages
|
|
|
|
"""
|
|
|
|
|
2010-07-19 07:54:26 +00:00
|
|
|
from copy import deepcopy
|
2017-01-20 02:27:16 +00:00
|
|
|
from allmydata.immutable.happiness_upload import share_placement, calculate_happiness
|
2010-07-19 07:54:26 +00:00
|
|
|
|
2010-05-14 00:49:17 +00:00
|
|
|
def failure_message(peer_count, k, happy, effective_happy):
|
|
|
|
# If peer_count < needed_shares, this error message makes more
|
|
|
|
# sense than any of the others, so use it.
|
|
|
|
if peer_count < k:
|
|
|
|
msg = ("shares could be placed or found on only %d "
|
|
|
|
"server(s). "
|
|
|
|
"We were asked to place shares on at least %d "
|
|
|
|
"server(s) such that any %d of them have "
|
|
|
|
"enough shares to recover the file." %
|
|
|
|
(peer_count, happy, k))
|
|
|
|
# Otherwise, if we've placed on at least needed_shares
|
|
|
|
# peers, but there isn't an x-happy subset of those peers
|
|
|
|
# for x >= needed_shares, we use this error message.
|
|
|
|
elif effective_happy < k:
|
|
|
|
msg = ("shares could be placed or found on %d "
|
|
|
|
"server(s), but they are not spread out evenly "
|
|
|
|
"enough to ensure that any %d of these servers "
|
|
|
|
"would have enough shares to recover the file. "
|
|
|
|
"We were asked to place "
|
|
|
|
"shares on at least %d servers such that any "
|
|
|
|
"%d of them have enough shares to recover the "
|
|
|
|
"file." %
|
|
|
|
(peer_count, k, happy, k))
|
|
|
|
# Otherwise, if there is an x-happy subset of peers where
|
2010-09-03 14:47:12 +00:00
|
|
|
# x >= needed_shares, but x < servers_of_happiness, then
|
2010-05-14 00:49:17 +00:00
|
|
|
# we use this message.
|
|
|
|
else:
|
|
|
|
msg = ("shares could be placed on only %d server(s) "
|
|
|
|
"such that any %d of them have enough shares "
|
|
|
|
"to recover the file, but we were asked to "
|
|
|
|
"place shares on at least %d such servers." %
|
|
|
|
(effective_happy, k, happy))
|
|
|
|
return msg
|
|
|
|
|
|
|
|
|
|
|
|
def shares_by_server(servermap):
|
|
|
|
"""
|
|
|
|
I accept a dict of shareid -> set(peerid) mappings, and return a
|
|
|
|
dict of peerid -> set(shareid) mappings. My argument is a dictionary
|
|
|
|
with sets of peers, indexed by shares, and I transform that into a
|
|
|
|
dictionary of sets of shares, indexed by peerids.
|
|
|
|
"""
|
|
|
|
ret = {}
|
|
|
|
for shareid, peers in servermap.iteritems():
|
|
|
|
assert isinstance(peers, set)
|
|
|
|
for peerid in peers:
|
|
|
|
ret.setdefault(peerid, set()).add(shareid)
|
|
|
|
return ret
|
|
|
|
|
2011-02-27 02:11:24 +00:00
|
|
|
def merge_servers(servermap, upload_trackers=None):
|
2010-05-14 00:49:17 +00:00
|
|
|
"""
|
2011-02-27 02:11:24 +00:00
|
|
|
I accept a dict of shareid -> set(serverid) mappings, and optionally a
|
|
|
|
set of ServerTrackers. If no set of ServerTrackers is provided, I return
|
2010-05-14 00:49:17 +00:00
|
|
|
my first argument unmodified. Otherwise, I update a copy of my first
|
2011-02-27 02:11:24 +00:00
|
|
|
argument to include the shareid -> serverid mappings implied in the
|
|
|
|
set of ServerTrackers, returning the resulting dict.
|
2010-05-14 00:49:17 +00:00
|
|
|
"""
|
2010-07-19 07:54:26 +00:00
|
|
|
# Since we mutate servermap, and are called outside of a
|
|
|
|
# context where it is okay to do that, make a copy of servermap and
|
|
|
|
# work with it.
|
|
|
|
servermap = deepcopy(servermap)
|
2011-02-27 02:11:11 +00:00
|
|
|
if not upload_trackers:
|
2010-05-14 00:49:17 +00:00
|
|
|
return servermap
|
|
|
|
|
|
|
|
assert(isinstance(servermap, dict))
|
2011-02-27 02:11:11 +00:00
|
|
|
assert(isinstance(upload_trackers, set))
|
2010-05-14 00:49:17 +00:00
|
|
|
|
2011-02-27 02:11:11 +00:00
|
|
|
for tracker in upload_trackers:
|
|
|
|
for shnum in tracker.buckets:
|
2011-02-27 02:11:38 +00:00
|
|
|
servermap.setdefault(shnum, set()).add(tracker.get_serverid())
|
2010-05-14 00:49:17 +00:00
|
|
|
return servermap
|
|
|
|
|
|
|
|
def servers_of_happiness(sharemap):
|
2017-01-20 02:27:16 +00:00
|
|
|
peers = sharemap.values()
|
|
|
|
if len(peers) == 1:
|
|
|
|
peers = peers[0]
|
|
|
|
else:
|
|
|
|
peers = [list(x)[0] for x in peers] # XXX
|
|
|
|
shares = sharemap.keys()
|
|
|
|
readonly_peers = set() # XXX
|
|
|
|
peers_to_shares = shares_by_server(sharemap)
|
|
|
|
places0 = share_placement(peers, readonly_peers, shares, peers_to_shares)
|
|
|
|
return calculate_happiness(places0)
|