|
@@ -513,6 +513,8 @@ class RemoteRepository:
|
|
self.chunkid_to_msgids = {}
|
|
self.chunkid_to_msgids = {}
|
|
self.ignore_responses = set()
|
|
self.ignore_responses = set()
|
|
self.responses = {}
|
|
self.responses = {}
|
|
|
|
+ self.async_responses = {}
|
|
|
|
+ self.shutdown_time = None
|
|
self.ratelimit = SleepingBandwidthLimiter(args.remote_ratelimit * 1024 if args and args.remote_ratelimit else 0)
|
|
self.ratelimit = SleepingBandwidthLimiter(args.remote_ratelimit * 1024 if args and args.remote_ratelimit else 0)
|
|
self.unpacker = get_limited_unpacker('client')
|
|
self.unpacker = get_limited_unpacker('client')
|
|
self.server_version = parse_version('1.0.8') # fallback version if server is too old to send version information
|
|
self.server_version = parse_version('1.0.8') # fallback version if server is too old to send version information
|
|
@@ -604,6 +606,7 @@ This problem will go away as soon as the server has been upgraded to 1.0.7+.
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
try:
|
|
try:
|
|
if exc_type is not None:
|
|
if exc_type is not None:
|
|
|
|
+ self.shutdown_time = time.monotonic() + 30
|
|
self.rollback()
|
|
self.rollback()
|
|
finally:
|
|
finally:
|
|
# in any case, we want to cleanly close the repo, even if the
|
|
# in any case, we want to cleanly close the repo, even if the
|
|
@@ -670,8 +673,8 @@ This problem will go away as soon as the server has been upgraded to 1.0.7+.
|
|
for resp in self.call_many(cmd, [args], **kw):
|
|
for resp in self.call_many(cmd, [args], **kw):
|
|
return resp
|
|
return resp
|
|
|
|
|
|
- def call_many(self, cmd, calls, wait=True, is_preloaded=False):
|
|
|
|
- if not calls:
|
|
|
|
|
|
+ def call_many(self, cmd, calls, wait=True, is_preloaded=False, async_wait=True):
|
|
|
|
+ if not calls and cmd != 'async_responses':
|
|
return
|
|
return
|
|
|
|
|
|
def pop_preload_msgid(chunkid):
|
|
def pop_preload_msgid(chunkid):
|
|
@@ -714,6 +717,12 @@ This problem will go away as soon as the server has been upgraded to 1.0.7+.
|
|
calls = list(calls)
|
|
calls = list(calls)
|
|
waiting_for = []
|
|
waiting_for = []
|
|
while wait or calls:
|
|
while wait or calls:
|
|
|
|
+ if self.shutdown_time and time.monotonic() > self.shutdown_time:
|
|
|
|
+ # we are shutting this RemoteRepository down already, make sure we do not waste
|
|
|
|
+ # a lot of time in case a lot of async stuff is coming in or remote is gone or slow.
|
|
|
|
+ logger.debug('shutdown_time reached, shutting down with %d waiting_for and %d async_responses.',
|
|
|
|
+ len(waiting_for), len(self.async_responses))
|
|
|
|
+ return
|
|
while waiting_for:
|
|
while waiting_for:
|
|
try:
|
|
try:
|
|
unpacked = self.responses.pop(waiting_for[0])
|
|
unpacked = self.responses.pop(waiting_for[0])
|
|
@@ -726,6 +735,22 @@ This problem will go away as soon as the server has been upgraded to 1.0.7+.
|
|
return
|
|
return
|
|
except KeyError:
|
|
except KeyError:
|
|
break
|
|
break
|
|
|
|
+ if cmd == 'async_responses':
|
|
|
|
+ while True:
|
|
|
|
+ try:
|
|
|
|
+ msgid, unpacked = self.async_responses.popitem()
|
|
|
|
+ except KeyError:
|
|
|
|
+ # there is nothing left what we already have received
|
|
|
|
+ if async_wait and self.ignore_responses:
|
|
|
|
+ # but do not return if we shall wait and there is something left to wait for:
|
|
|
|
+ break
|
|
|
|
+ else:
|
|
|
|
+ return
|
|
|
|
+ else:
|
|
|
|
+ if b'exception_class' in unpacked:
|
|
|
|
+ handle_error(unpacked)
|
|
|
|
+ else:
|
|
|
|
+ yield unpacked[RESULT]
|
|
if self.to_send or ((calls or self.preload_ids) and len(waiting_for) < MAX_INFLIGHT):
|
|
if self.to_send or ((calls or self.preload_ids) and len(waiting_for) < MAX_INFLIGHT):
|
|
w_fds = [self.stdin_fd]
|
|
w_fds = [self.stdin_fd]
|
|
else:
|
|
else:
|
|
@@ -755,8 +780,14 @@ This problem will go away as soon as the server has been upgraded to 1.0.7+.
|
|
raise UnexpectedRPCDataFormatFromServer(data)
|
|
raise UnexpectedRPCDataFormatFromServer(data)
|
|
if msgid in self.ignore_responses:
|
|
if msgid in self.ignore_responses:
|
|
self.ignore_responses.remove(msgid)
|
|
self.ignore_responses.remove(msgid)
|
|
|
|
+ # async methods never return values, but may raise exceptions.
|
|
if b'exception_class' in unpacked:
|
|
if b'exception_class' in unpacked:
|
|
- handle_error(unpacked)
|
|
|
|
|
|
+ self.async_responses[msgid] = unpacked
|
|
|
|
+ else:
|
|
|
|
+ # we currently do not have async result values except "None",
|
|
|
|
+ # so we do not add them into async_responses.
|
|
|
|
+ if unpacked[RESULT] is not None:
|
|
|
|
+ self.async_responses[msgid] = unpacked
|
|
else:
|
|
else:
|
|
self.responses[msgid] = unpacked
|
|
self.responses[msgid] = unpacked
|
|
elif fd is self.stderr_fd:
|
|
elif fd is self.stderr_fd:
|
|
@@ -805,7 +836,7 @@ This problem will go away as soon as the server has been upgraded to 1.0.7+.
|
|
# that the fd should be writable
|
|
# that the fd should be writable
|
|
if e.errno != errno.EAGAIN:
|
|
if e.errno != errno.EAGAIN:
|
|
raise
|
|
raise
|
|
- self.ignore_responses |= set(waiting_for)
|
|
|
|
|
|
+ self.ignore_responses |= set(waiting_for) # we lose order here
|
|
|
|
|
|
@api(since=parse_version('1.0.0'),
|
|
@api(since=parse_version('1.0.0'),
|
|
append_only={'since': parse_version('1.0.7'), 'previously': False})
|
|
append_only={'since': parse_version('1.0.7'), 'previously': False})
|
|
@@ -883,6 +914,10 @@ This problem will go away as soon as the server has been upgraded to 1.0.7+.
|
|
self.p.wait()
|
|
self.p.wait()
|
|
self.p = None
|
|
self.p = None
|
|
|
|
|
|
|
|
+ def async_response(self, wait=True):
|
|
|
|
+ for resp in self.call_many('async_responses', calls=[], wait=True, async_wait=wait):
|
|
|
|
+ return resp
|
|
|
|
+
|
|
def preload(self, ids):
|
|
def preload(self, ids):
|
|
self.preload_ids += ids
|
|
self.preload_ids += ids
|
|
|
|
|