]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/mutable/common.py
Mutable repair: use new MODE_REPAIR to query all servers *and* get privkey
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / mutable / common.py
1
2 from allmydata.util.spans import DataSpans
3
4 MODE_CHECK = "MODE_CHECK" # query all peers
5 MODE_ANYTHING = "MODE_ANYTHING" # one recoverable version
6 MODE_WRITE = "MODE_WRITE" # replace all shares, probably.. not for initial
7                           # creation
8 MODE_READ = "MODE_READ"
9 MODE_REPAIR = "MODE_REPAIR" # query all peers, get the privkey
10
11 class NotWriteableError(Exception):
12     pass
13
14 class BadShareError(Exception):
15     """This represents an error discovered in a particular share, during
16     retrieve, from which we can recover by using some other share. This does
17     *not* include local coding errors.
18     """
19
20 class NeedMoreDataError(BadShareError):
21     def __init__(self, needed_bytes, encprivkey_offset, encprivkey_length):
22         Exception.__init__(self)
23         self.needed_bytes = needed_bytes # up through EOF
24         self.encprivkey_offset = encprivkey_offset
25         self.encprivkey_length = encprivkey_length
26     def __repr__(self):
27         return "<NeedMoreDataError (%d bytes)>" % self.needed_bytes
28
29 class UncoordinatedWriteError(Exception):
30     def __repr__(self):
31         return ("<%s -- You, oh user, tried to change a file or directory "
32                 "at the same time as another process was trying to change it. "
33                 " To avoid data loss, don't do this.  Please see "
34                 "docs/write_coordination.rst for details.>" %
35                 (self.__class__.__name__,))
36
37 class UnrecoverableFileError(Exception):
38     pass
39
40 class NotEnoughServersError(Exception):
41     """There were not enough functioning servers available to place shares
42     upon. This might result from all servers being full or having an error, a
43     local bug which causes all server requests to fail in the same way, or
44     from there being zero servers. The first error received (if any) is
45     stored in my .first_error attribute."""
46     def __init__(self, why, first_error=None):
47         Exception.__init__(self, why, first_error)
48         self.first_error = first_error
49
50 class CorruptShareError(BadShareError):
51     def __init__(self, server, shnum, reason):
52         self.args = (server, shnum, reason)
53         self.server = server
54         self.shnum = shnum
55         self.reason = reason
56     def __str__(self):
57         return "<CorruptShareError server=%s shnum[%d]: %s" % \
58                (self.server.get_name(), self.shnum, self.reason)
59
60 class UnknownVersionError(BadShareError):
61     """The share we received was of a version we don't recognize."""
62
63 class ResponseCache:
64     """I cache share data, to reduce the number of round trips used during
65     mutable file operations. All of the data in my cache is for a single
66     storage index, but I will keep information on multiple shares for
67     that storage index.
68
69     I maintain a highest-seen sequence number, and will flush all entries
70     each time this number increases (this doesn't necessarily imply that
71     all entries have the same sequence number).
72
73     My cache is indexed by a (verinfo, shnum) tuple.
74
75     My cache entries are DataSpans instances, each representing a set of
76     non-overlapping byteranges.
77     """
78
79     def __init__(self):
80         self.cache = {}
81         self.seqnum = None
82
83     def _clear(self):
84         # also used by unit tests
85         self.cache = {}
86
87     def add(self, verinfo, shnum, offset, data):
88         seqnum = verinfo[0]
89         if seqnum > self.seqnum:
90             self._clear()
91             self.seqnum = seqnum
92
93         index = (verinfo, shnum)
94         if index in self.cache:
95             self.cache[index].add(offset, data)
96         else:
97             spans = DataSpans()
98             spans.add(offset, data)
99             self.cache[index] = spans
100
101     def read(self, verinfo, shnum, offset, length):
102         """Try to satisfy a read request from cache.
103         Returns data, or None if the cache did not hold the entire requested span.
104         """
105
106         # TODO: perhaps return a DataSpans object representing the fragments
107         # that we have, instead of only returning a hit if we can satisfy the
108         # whole request from cache.
109
110         index = (verinfo, shnum)
111         if index in self.cache:
112             return self.cache[index].get(offset, length)
113         else:
114             return None