Auto merge of #2517 - micbou:remove-healthy-check, r=Valloric
[READY] Remove healthy check This PR removes the code that, if the server is not yet healthy, tries in a separate thread to send the same request at different intervals until it gets a response or reaches 5 failed attempts. Main issue with that code is that it blocks Vim if a synchronous request (`semantic_completion_available`, `run_completer_command`, etc.) is sent while the server is not up. See issue #2071. Other issues are that it's hard to understand how this code behaves (it combines threads and retries), it becomes useless once the server is healthy (because `SERVER_HEALTHY` is cached and never reset), it depends on arbitrary parameters (why 5 retries with a delay of 0.5s multiplied by 1.5 at each attempt?), and it has a package dependency `retries`. Now that we catch all server exceptions (PR #2453) and define a very short connection timeout (PR #2514), we can just drop this code. This change gives a huge improvement in startup time on Windows (don't worry, more improvements are coming for all platforms). The reason is that `requests` (probably `urllib3`) tries for ~1s to check if the server is healthy on Windows while it's almost instantaneous on other platforms. <table> <tr> <th rowspan="2">Platform</th> <th colspan="2">First run (ms)</th> <th colspan="2">Subsequent runs (ms)</th> </tr> <tr> <td>Before</td> <td>After</td> <td>Before</td> <td>After</td> </tr> <tr> <td>Ubuntu 16.04 64-bit</td> <td>228</td> <td>248</td> <td>176</td> <td>151</td> </tr> <tr> <td>macOS 10.12</td> <td>433</td> <td>417</td> <td>263</td> <td>263</td> </tr> <tr> <td>Windows 10 64-bit</td> <td>1861</td> <td>844</td> <td>814</td> <td>292</td> </tr> </table> These results were obtained by running the `prof.py` script from [this branch](https://github.com/micbou/YouCompleteMe/tree/profiling-startup). The difference between first run and subsequent runs is Python bytecode generation (`*.pyc` files). Fixes #2071. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/valloric/youcompleteme/2517) <!-- Reviewable:end -->
This commit is contained in:
commit
f6867340dd
@ -30,7 +30,6 @@ import urllib.parse
|
|||||||
import json
|
import json
|
||||||
from future.utils import native
|
from future.utils import native
|
||||||
from base64 import b64decode, b64encode
|
from base64 import b64decode, b64encode
|
||||||
from retries import retries
|
|
||||||
from requests_futures.sessions import FuturesSession
|
from requests_futures.sessions import FuturesSession
|
||||||
from ycm.unsafe_thread_pool_executor import UnsafeThreadPoolExecutor
|
from ycm.unsafe_thread_pool_executor import UnsafeThreadPoolExecutor
|
||||||
from ycm import vimsupport
|
from ycm import vimsupport
|
||||||
@ -102,43 +101,20 @@ class BaseRequest( object ):
|
|||||||
handler,
|
handler,
|
||||||
method,
|
method,
|
||||||
timeout = _READ_TIMEOUT_SEC ):
|
timeout = _READ_TIMEOUT_SEC ):
|
||||||
def SendRequest( data, handler, method, timeout ):
|
request_uri = _BuildUri( handler )
|
||||||
request_uri = _BuildUri( handler )
|
if method == 'POST':
|
||||||
if method == 'POST':
|
sent_data = _ToUtf8Json( data )
|
||||||
sent_data = _ToUtf8Json( data )
|
return BaseRequest.session.post(
|
||||||
return BaseRequest.session.post(
|
request_uri,
|
||||||
request_uri,
|
data = sent_data,
|
||||||
data = sent_data,
|
headers = BaseRequest._ExtraHeaders( method,
|
||||||
headers = BaseRequest._ExtraHeaders( method,
|
request_uri,
|
||||||
request_uri,
|
sent_data ),
|
||||||
sent_data ),
|
timeout = ( _CONNECT_TIMEOUT_SEC, timeout ) )
|
||||||
timeout = ( _CONNECT_TIMEOUT_SEC, timeout ) )
|
return BaseRequest.session.get(
|
||||||
if method == 'GET':
|
request_uri,
|
||||||
return BaseRequest.session.get(
|
headers = BaseRequest._ExtraHeaders( method, request_uri ),
|
||||||
request_uri,
|
timeout = ( _CONNECT_TIMEOUT_SEC, timeout ) )
|
||||||
headers = BaseRequest._ExtraHeaders( method, request_uri ),
|
|
||||||
timeout = ( _CONNECT_TIMEOUT_SEC, timeout ) )
|
|
||||||
|
|
||||||
@retries( 5, delay = 0.5, backoff = 1.5 )
|
|
||||||
def DelayedSendRequest( data, handler, method ):
|
|
||||||
request_uri = _BuildUri( handler )
|
|
||||||
if method == 'POST':
|
|
||||||
sent_data = _ToUtf8Json( data )
|
|
||||||
return requests.post(
|
|
||||||
request_uri,
|
|
||||||
data = sent_data,
|
|
||||||
headers = BaseRequest._ExtraHeaders( method,
|
|
||||||
request_uri,
|
|
||||||
sent_data ) )
|
|
||||||
if method == 'GET':
|
|
||||||
return requests.get(
|
|
||||||
request_uri,
|
|
||||||
headers = BaseRequest._ExtraHeaders( method, request_uri ) )
|
|
||||||
|
|
||||||
if not _CheckServerIsHealthyWithCache():
|
|
||||||
return _EXECUTOR.submit( DelayedSendRequest, data, handler, method )
|
|
||||||
|
|
||||||
return SendRequest( data, handler, method, timeout )
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -271,31 +247,6 @@ def _BuildUri( handler ):
|
|||||||
handler ) ) )
|
handler ) ) )
|
||||||
|
|
||||||
|
|
||||||
SERVER_HEALTHY = False
|
|
||||||
|
|
||||||
|
|
||||||
def _CheckServerIsHealthyWithCache():
|
|
||||||
global SERVER_HEALTHY
|
|
||||||
|
|
||||||
def _ServerIsHealthy():
|
|
||||||
request_uri = _BuildUri( 'healthy' )
|
|
||||||
response = requests.get( request_uri,
|
|
||||||
headers = BaseRequest._ExtraHeaders(
|
|
||||||
'GET', request_uri, bytes( b'' ) ) )
|
|
||||||
_ValidateResponseObject( response )
|
|
||||||
response.raise_for_status()
|
|
||||||
return response.json()
|
|
||||||
|
|
||||||
if SERVER_HEALTHY:
|
|
||||||
return True
|
|
||||||
|
|
||||||
try:
|
|
||||||
SERVER_HEALTHY = _ServerIsHealthy()
|
|
||||||
return SERVER_HEALTHY
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def MakeServerException( data ):
|
def MakeServerException( data ):
|
||||||
if data[ 'exception' ][ 'TYPE' ] == UnknownExtraConf.__name__:
|
if data[ 'exception' ][ 'TYPE' ] == UnknownExtraConf.__name__:
|
||||||
return UnknownExtraConf( data[ 'exception' ][ 'extra_conf_file' ] )
|
return UnknownExtraConf( data[ 'exception' ][ 'extra_conf_file' ] )
|
||||||
|
79
third_party/retries/retries.py
vendored
79
third_party/retries/retries.py
vendored
@ -1,79 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
#
|
|
||||||
# Copyright 2012 by Jeff Laughlin Consulting LLC
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in
|
|
||||||
# all copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
# SOFTWARE.
|
|
||||||
|
|
||||||
|
|
||||||
import sys
|
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
# Source: https://gist.github.com/n1ywb/2570004
|
|
||||||
|
|
||||||
def example_exc_handler(tries_remaining, exception, delay):
|
|
||||||
"""Example exception handler; prints a warning to stderr.
|
|
||||||
|
|
||||||
tries_remaining: The number of tries remaining.
|
|
||||||
exception: The exception instance which was raised.
|
|
||||||
"""
|
|
||||||
print >> sys.stderr, "Caught '%s', %d tries remaining, sleeping for %s seconds" % (exception, tries_remaining, delay)
|
|
||||||
|
|
||||||
|
|
||||||
def retries(max_tries, delay=1, backoff=2, exceptions=(Exception,), hook=None):
|
|
||||||
"""Function decorator implementing retrying logic.
|
|
||||||
|
|
||||||
delay: Sleep this many seconds * backoff * try number after failure
|
|
||||||
backoff: Multiply delay by this factor after each failure
|
|
||||||
exceptions: A tuple of exception classes; default (Exception,)
|
|
||||||
hook: A function with the signature myhook(tries_remaining, exception);
|
|
||||||
default None
|
|
||||||
|
|
||||||
The decorator will call the function up to max_tries times if it raises
|
|
||||||
an exception.
|
|
||||||
|
|
||||||
By default it catches instances of the Exception class and subclasses.
|
|
||||||
This will recover after all but the most fatal errors. You may specify a
|
|
||||||
custom tuple of exception classes with the 'exceptions' argument; the
|
|
||||||
function will only be retried if it raises one of the specified
|
|
||||||
exceptions.
|
|
||||||
|
|
||||||
Additionally you may specify a hook function which will be called prior
|
|
||||||
to retrying with the number of remaining tries and the exception instance;
|
|
||||||
see given example. This is primarily intended to give the opportunity to
|
|
||||||
log the failure. Hook is not called after failure if no retries remain.
|
|
||||||
"""
|
|
||||||
def dec(func):
|
|
||||||
def f2(*args, **kwargs):
|
|
||||||
mydelay = delay
|
|
||||||
tries = reversed(range(max_tries))
|
|
||||||
for tries_remaining in tries:
|
|
||||||
try:
|
|
||||||
return func(*args, **kwargs)
|
|
||||||
except exceptions as e:
|
|
||||||
if tries_remaining > 0:
|
|
||||||
if hook is not None:
|
|
||||||
hook(tries_remaining, e, mydelay)
|
|
||||||
sleep(mydelay)
|
|
||||||
mydelay = mydelay * backoff
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
return f2
|
|
||||||
return dec
|
|
Loading…
Reference in New Issue
Block a user