##// END OF EJS Templates
remove appendfile data as soon as not needed.
Vadim Gelfer -
r2102:c6211281 default
parent child Browse files
Show More
@@ -1,157 +1,154 b''
1 1 # appendfile.py - special classes to make repo updates atomic
2 2 #
3 3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from demandload import *
9 9 demandload(globals(), "cStringIO changelog errno manifest os tempfile")
10 10
11 11 # writes to metadata files are ordered. reads: changelog, manifest,
12 12 # normal files. writes: normal files, manifest, changelog.
13 13
14 14 # manifest contains pointers to offsets in normal files. changelog
15 15 # contains pointers to offsets in manifest. if reader reads old
16 16 # changelog while manifest or normal files are written, it has no
17 17 # pointers into new parts of those files that are maybe not consistent
18 18 # yet, so will not read them.
19 19
20 20 # localrepo.addchangegroup thinks it writes changelog first, then
21 21 # manifest, then normal files (this is order they are available, and
22 22 # needed for computing linkrev fields), but uses appendfile to hide
23 23 # updates from readers. data not written to manifest or changelog
24 24 # until all normal files updated. write manifest first, then
25 25 # changelog.
26 26
27 27 # with this write ordering, readers cannot see inconsistent view of
28 28 # repo during update.
29 29
30 30 class appendfile(object):
31 31 '''implement enough of file protocol to append to revlog file.
32 32 appended data is written to temp file. reads and seeks span real
33 33 file and temp file. readers cannot see appended data until
34 34 writedata called.'''
35 35
36 36 def __init__(self, fp, tmpname):
37 37 if tmpname:
38 38 self.tmpname = tmpname
39 39 self.tmpfp = open(self.tmpname, 'ab+')
40 40 else:
41 41 fd, self.tmpname = tempfile.mkstemp()
42 42 self.tmpfp = os.fdopen(fd, 'ab+')
43 43 self.realfp = fp
44 44 self.offset = fp.tell()
45 45 # real file is not written by anyone else. cache its size so
46 46 # seek and read can be fast.
47 47 self.realsize = os.fstat(fp.fileno()).st_size
48 48
49 49 def end(self):
50 50 self.tmpfp.flush() # make sure the stat is correct
51 51 return self.realsize + os.fstat(self.tmpfp.fileno()).st_size
52 52
53 53 def tell(self):
54 54 return self.offset
55 55
56 56 def flush(self):
57 57 self.tmpfp.flush()
58 58
59 59 def close(self):
60 60 self.realfp.close()
61 61 self.tmpfp.close()
62 62
63 63 def seek(self, offset, whence=0):
64 64 '''virtual file offset spans real file and temp file.'''
65 65 if whence == 0:
66 66 self.offset = offset
67 67 elif whence == 1:
68 68 self.offset += offset
69 69 elif whence == 2:
70 70 self.offset = self.end() + offset
71 71
72 72 if self.offset < self.realsize:
73 73 self.realfp.seek(self.offset)
74 74 else:
75 75 self.tmpfp.seek(self.offset - self.realsize)
76 76
77 77 def read(self, count=-1):
78 78 '''only trick here is reads that span real file and temp file.'''
79 79 fp = cStringIO.StringIO()
80 80 old_offset = self.offset
81 81 if self.offset < self.realsize:
82 82 s = self.realfp.read(count)
83 83 fp.write(s)
84 84 self.offset += len(s)
85 85 if count > 0:
86 86 count -= len(s)
87 87 if count != 0:
88 88 if old_offset != self.offset:
89 89 self.tmpfp.seek(self.offset - self.realsize)
90 90 s = self.tmpfp.read(count)
91 91 fp.write(s)
92 92 self.offset += len(s)
93 93 return fp.getvalue()
94 94
95 95 def write(self, s):
96 96 '''append to temp file.'''
97 97 self.tmpfp.seek(0, 2)
98 98 self.tmpfp.write(s)
99 99 # all writes are appends, so offset must go to end of file.
100 100 self.offset = self.realsize + self.tmpfp.tell()
101 101
102 102 class appendopener(object):
103 103 '''special opener for files that only read or append.'''
104 104
105 105 def __init__(self, opener):
106 106 self.realopener = opener
107 107 # key: file name, value: appendfile name
108 108 self.tmpnames = {}
109 109
110 110 def __call__(self, name, mode='r'):
111 111 '''open file.'''
112 112
113 113 assert mode in 'ra+'
114 114 try:
115 115 realfp = self.realopener(name, 'r')
116 116 except IOError, err:
117 117 if err.errno != errno.ENOENT: raise
118 118 realfp = self.realopener(name, 'w+')
119 119 tmpname = self.tmpnames.get(name)
120 120 fp = appendfile(realfp, tmpname)
121 121 if tmpname is None:
122 122 self.tmpnames[name] = fp.tmpname
123 123 return fp
124 124
125 125 def writedata(self):
126 126 '''copy data from temp files to real files.'''
127 127 # write .d file before .i file.
128 128 tmpnames = self.tmpnames.items()
129 129 tmpnames.sort()
130 130 for name, tmpname in tmpnames:
131 131 fp = open(tmpname, 'rb')
132 132 s = fp.read()
133 133 fp.close()
134 os.unlink(tmpname)
134 135 fp = self.realopener(name, 'a')
135 136 fp.write(s)
136 137 fp.close()
137 138
138 def __del__(self):
139 for tmpname in self.tmpnames.itervalues():
140 os.unlink(tmpname)
141
142 139 # files for changelog and manifest are in different appendopeners, so
143 140 # not mixed up together.
144 141
145 142 class appendchangelog(changelog.changelog, appendopener):
146 143 def __init__(self, opener, version):
147 144 appendopener.__init__(self, opener)
148 145 changelog.changelog.__init__(self, self, version)
149 146 def checkinlinesize(self, fp, tr):
150 147 return
151 148
152 149 class appendmanifest(manifest.manifest, appendopener):
153 150 def __init__(self, opener, version):
154 151 appendopener.__init__(self, opener)
155 152 manifest.manifest.__init__(self, self, version)
156 153 def checkinlinesize(self, fp, tr):
157 154 return
General Comments 0
You need to be logged in to leave comments. Login now