source file: /home/buildslave/tahoe/edgy/build/src/allmydata/mutable/common.py
file stats: 77 lines, 75 executed: 97.4% covered
1.
2. from allmydata.util import idlib
3. from allmydata.util.dictutil import DictOfSets
4.
5. MODE_CHECK = "MODE_CHECK" # query all peers
6. MODE_ANYTHING = "MODE_ANYTHING" # one recoverable version
7. MODE_WRITE = "MODE_WRITE" # replace all shares, probably.. not for initial
8. # creation
9. MODE_READ = "MODE_READ"
10.
11. class NotMutableError(Exception):
12. pass
13.
14. class NeedMoreDataError(Exception):
15. def __init__(self, needed_bytes, encprivkey_offset, encprivkey_length):
16. Exception.__init__(self)
17. self.needed_bytes = needed_bytes # up through EOF
18. self.encprivkey_offset = encprivkey_offset
19. self.encprivkey_length = encprivkey_length
20. def __repr__(self):
21. return "<NeedMoreDataError (%d bytes)>" % self.needed_bytes
22.
23. class UncoordinatedWriteError(Exception):
24. def __repr__(self):
25. return ("<%s -- You, oh user, tried to change a file or directory "
26. "at the same time as another process was trying to change it. "
27. " To avoid data loss, don't do this. Please see "
28. "docs/write_coordination.html for details.>" %
29. (self.__class__.__name__,))
30.
31. class UnrecoverableFileError(Exception):
32. pass
33.
34. class NotEnoughServersError(Exception):
35. """There were not enough functioning servers available to place shares
36. upon."""
37.
38. class CorruptShareError(Exception):
39. def __init__(self, peerid, shnum, reason):
40. self.args = (peerid, shnum, reason)
41. self.peerid = peerid
42. self.shnum = shnum
43. self.reason = reason
44. def __str__(self):
45. short_peerid = idlib.nodeid_b2a(self.peerid)[:8]
46. return "<CorruptShareError peerid=%s shnum[%d]: %s" % (short_peerid,
47. self.shnum,
48. self.reason)
49.
50.
51.
52. class ResponseCache:
53. """I cache share data, to reduce the number of round trips used during
54. mutable file operations. All of the data in my cache is for a single
55. storage index, but I will keep information on multiple shares (and
56. multiple versions) for that storage index.
57.
58. My cache is indexed by a (verinfo, shnum) tuple.
59.
60. My cache entries contain a set of non-overlapping byteranges: (start,
61. data, timestamp) tuples.
62. """
63.
64. def __init__(self):
65. self.cache = DictOfSets()
66.
67. def _clear(self):
68. # used by unit tests
69. self.cache = DictOfSets()
70.
71. def _does_overlap(self, x_start, x_length, y_start, y_length):
72. if x_start < y_start:
73. x_start, y_start = y_start, x_start
74. x_length, y_length = y_length, x_length
75. x_end = x_start + x_length
76. y_end = y_start + y_length
77. # this just returns a boolean. Eventually we'll want a form that
78. # returns a range.
79. if not x_length:
80. return False
81. if not y_length:
82. return False
83. if x_start >= y_end:
84. return False
85. if y_start >= x_end:
86. return False
87. return True
88.
89.
90. def _inside(self, x_start, x_length, y_start, y_length):
91. x_end = x_start + x_length
92. y_end = y_start + y_length
93. if x_start < y_start:
94. return False
95. if x_start >= y_end:
96. return False
97. if x_end < y_start:
98. return False
99. if x_end > y_end:
100. return False
101. return True
102.
103. def add(self, verinfo, shnum, offset, data, timestamp):
104. index = (verinfo, shnum)
105. self.cache.add(index, (offset, data, timestamp) )
106.
107. def read(self, verinfo, shnum, offset, length):
108. """Try to satisfy a read request from cache.
109. Returns (data, timestamp), or (None, None) if the cache did not hold
110. the requested data.
111. """
112.
113. # TODO: join multiple fragments, instead of only returning a hit if
114. # we have a fragment that contains the whole request
115.
116. index = (verinfo, shnum)
117. end = offset+length
118. for entry in self.cache.get(index, set()):
119. (e_start, e_data, e_timestamp) = entry
120. if self._inside(offset, length, e_start, len(e_data)):
121. want_start = offset - e_start
122. want_end = offset+length - e_start
123. return (e_data[want_start:want_end], e_timestamp)
124. return None, None
125.
126.