##// END OF EJS Templates
archive-caches: refactor and use cachedir based archive generation
super-admin -
r1122:44baf3db python3
parent child Browse files
Show More
@@ -16,10 +16,12 b''
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 import os
17 import os
18 import sys
18 import sys
19 import tempfile
19 import traceback
20 import traceback
20 import logging
21 import logging
21 import urllib.parse
22 import urllib.parse
22
23
24 from vcsserver.lib.rc_cache.archive_cache import get_archival_cache_store
23 from vcsserver.lib.rc_cache import region_meta
25 from vcsserver.lib.rc_cache import region_meta
24
26
25 from vcsserver import exceptions
27 from vcsserver import exceptions
@@ -84,6 +86,7 b' def raise_from_original(new_type, org_ex'
84 del exc_traceback
86 del exc_traceback
85
87
86
88
89
87 class ArchiveNode(object):
90 class ArchiveNode(object):
88 def __init__(self, path, mode, is_link, raw_bytes):
91 def __init__(self, path, mode, is_link, raw_bytes):
89 self.path = path
92 self.path = path
@@ -92,28 +95,51 b' class ArchiveNode(object):'
92 self.raw_bytes = raw_bytes
95 self.raw_bytes = raw_bytes
93
96
94
97
95 def archive_repo(walker, archive_dest_path, kind, mtime, archive_at_path,
98 def store_archive_in_cache(node_walker, archive_key, kind, mtime, archive_at_path, archive_dir_name,
96 archive_dir_name, commit_id, write_metadata=True, extra_metadata=None):
99 commit_id, write_metadata=True, extra_metadata=None, cache_config=None):
97 """
100 """
101 Function that would store an generate archive and send it to a dedicated backend store
102 In here we use diskcache
103
104 :param node_walker: a generator returning nodes to add to archive
105 :param archive_key: key used to store the path
106 :param kind: archive kind
107 :param mtime: time of creation
108 :param archive_at_path: default '/' the path at archive was started. if this is not '/' it means it's a partial archive
109 :param archive_dir_name: inside dir name when creating an archive
110 :param commit_id: commit sha of revision archive was created at
111 :param write_metadata:
112 :param extra_metadata:
113 :param cache_config:
114
98 walker should be a file walker, for example:
115 walker should be a file walker, for example:
99 def walker():
116 def node_walker():
100 for file_info in files:
117 for file_info in files:
101 yield ArchiveNode(fn, mode, is_link, ctx[fn].data)
118 yield ArchiveNode(fn, mode, is_link, ctx[fn].data)
102 """
119 """
103 extra_metadata = extra_metadata or {}
120 extra_metadata = extra_metadata or {}
104 archive_dest_path = safe_bytes(archive_dest_path)
121
122 d_cache = get_archival_cache_store(config=cache_config)
123
124 if archive_key in d_cache:
125 with d_cache as d_cache_reader:
126 reader, tag = d_cache_reader.get(archive_key, read=True, tag=True, retry=True)
127 return reader.name
128
129 archive_tmp_path = safe_bytes(tempfile.mkstemp()[1])
130 log.debug('Creating new temp archive in %s', archive_tmp_path)
105
131
106 if kind == "tgz":
132 if kind == "tgz":
107 archiver = archival.tarit(archive_dest_path, mtime, b"gz")
133 archiver = archival.tarit(archive_tmp_path, mtime, b"gz")
108 elif kind == "tbz2":
134 elif kind == "tbz2":
109 archiver = archival.tarit(archive_dest_path, mtime, b"bz2")
135 archiver = archival.tarit(archive_tmp_path, mtime, b"bz2")
110 elif kind == 'zip':
136 elif kind == 'zip':
111 archiver = archival.zipit(archive_dest_path, mtime)
137 archiver = archival.zipit(archive_tmp_path, mtime)
112 else:
138 else:
113 raise exceptions.ArchiveException()(
139 raise exceptions.ArchiveException()(
114 f'Remote does not support: "{kind}" archive type.')
140 f'Remote does not support: "{kind}" archive type.')
115
141
116 for f in walker(commit_id, archive_at_path):
142 for f in node_walker(commit_id, archive_at_path):
117 f_path = os.path.join(safe_bytes(archive_dir_name), safe_bytes(f.path).lstrip(b'/'))
143 f_path = os.path.join(safe_bytes(archive_dir_name), safe_bytes(f.path).lstrip(b'/'))
118 try:
144 try:
119 archiver.addfile(f_path, f.mode, f.is_link, f.raw_bytes())
145 archiver.addfile(f_path, f.mode, f.is_link, f.raw_bytes())
@@ -133,46 +159,37 b' def archive_repo(walker, archive_dest_pa'
133 f_path = os.path.join(safe_bytes(archive_dir_name), b'.archival.txt')
159 f_path = os.path.join(safe_bytes(archive_dir_name), b'.archival.txt')
134 archiver.addfile(f_path, 0o644, False, b'\n'.join(meta))
160 archiver.addfile(f_path, 0o644, False, b'\n'.join(meta))
135
161
136 return archiver.done()
162 archiver.done()
163
164 # ensure set & get are atomic
165 with d_cache.transact():
166
167 with open(archive_tmp_path, 'rb') as archive_file:
168 add_result = d_cache.set(archive_key, archive_file, read=True, tag='db-name', retry=True)
169 if not add_result:
170 log.error('Failed to store cache for key=%s', archive_key)
171
172 os.remove(archive_tmp_path)
173
174 reader, tag = d_cache.get(archive_key, read=True, tag=True, retry=True)
175 if not reader:
176 raise AssertionError(f'empty reader on key={archive_key} added={add_result}')
177
178 return reader.name
137
179
138
180
139 class BinaryEnvelope(object):
181 class BinaryEnvelope(object):
140 def __init__(self, value: bytes, bin_type=True):
182 def __init__(self, val):
141 self.value = value
183 self.val = val
142 self.bin_type = bin_type
143
144 def __len__(self):
145 return len(self.value)
146
147 def __getitem__(self, index):
148 return self.value[index]
149
150 def __iter__(self):
151 return iter(self.value)
152
153 def __str__(self):
154 return str(self.value)
155
156 def __repr__(self):
157 return repr(self.value)
158
159 def __eq__(self, other):
160 if isinstance(other, BinaryEnvelope):
161 return self.value == other.value
162 return False
163
164 def __ne__(self, other):
165 return not self.__eq__(other)
166
167 def __add__(self, other):
168 if isinstance(other, BinaryEnvelope):
169 return BinaryEnvelope(self.value + other.value)
170 raise TypeError(f"unsupported operand type(s) for +: 'BinaryEnvelope' and '{type(other)}'")
171
172 def __radd__(self, other):
173 if isinstance(other, BinaryEnvelope):
174 return BinaryEnvelope(other.value + self.value)
175 raise TypeError(f"unsupported operand type(s) for +: '{type(other)}' and 'BinaryEnvelope'")
176
184
177
185
186 class BytesEnvelope(bytes):
187 def __new__(cls, content):
188 if isinstance(content, bytes):
189 return super().__new__(cls, content)
190 else:
191 raise TypeError('Content must be bytes.')
178
192
193
194 class BinaryBytesEnvelope(BytesEnvelope):
195 pass
General Comments 0
You need to be logged in to leave comments. Login now