# Buggy code
def perform_request(self, method, url, params=None, body=None):
    """
    Perform the actual request. Retrieve a connection from the connection
    pool, pass all the information to it's perform_request method and
    return the data.

    If an exception was raised, mark the connection as failed and retry (up
    to `max_retries` times).

    If the operation was succesful and the connection used was previously
    marked as dead, mark it as live, resetting it's failure count.

    :arg method: HTTP method to use
    :arg url: absolute url (without host) to target
    :arg params: dictionary of query parameters, will be handed over to the
        underlying :class:`~elasticsearch.Connection` class for serialization
    :arg body: body of the request, will be serializes using serializer and
        passed to the connection
    """
    if body is not None:
        body = self.serializer.dumps(body)

        # some clients or environments don't support sending GET with body
        if method == "GET" and self.send_get_body_as != "GET":
            # send it as post instead
            if self.send_get_body_as == "POST":
                method = "POST"

            # or as source parameter
            elif self.send_get_body_as == "source":
                if params is None:
                    params = {}
                params["source"] = body
                body = None

    if body is not None:
        try:
            body = body.encode("utf-8")
        except (UnicodeDecodeError, AttributeError):
            # bytes/str - no need to re-encode
            pass

    ignore = ()
    timeout = None
    if params:
        timeout = params.pop("request_timeout", None)
        ignore = params.pop("ignore", ())
        if isinstance(ignore, int):
            ignore = (ignore,)

    for attempt in range(self.max_retries + 1):
        connection = self.get_connection()

        try:
            status, headers, data = connection.perform_request(
                method, url, params, body, ignore=ignore, timeout=timeout
            )

        except TransportError as e:
            retry = False
            if isinstance(e, ConnectionTimeout):
                retry = self.retry_on_timeout
            elif isinstance(e, ConnectionError):
                retry = True
            elif e.status_code in self.retry_on_status:
                retry = True

            if retry:
                # only mark as dead if we are retrying
                self.mark_dead(connection)
                # raise exception on last retry
                if attempt == self.max_retries:
                    raise
            else:
                raise

        else:
            # connection didn't fail, confirm it's live status
            self.connection_pool.mark_live(connection)
            if data:
                data = self.deserializer.loads(data, headers.get("content-type"))
            return status, data


# Traceback (elasticsearch.exceptions.NotFoundError)
es.search_exists('my_index', 'my_type', params={'q': 'my_field:bar'})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.7/dist-packages/elasticsearch/client/utils.py", line 68, in _wrapped
return func(*args, params=params, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/elasticsearch/client/__init__.py", line 1238, in search_exists
doc_type, '_search', 'exists'), params=params, body=body)
File "/usr/local/lib/python2.7/dist-packages/elasticsearch/transport.py", line 307, in perform_request
status, headers, data = connection.perform_request(method, url, params, body, ignore=ignore, timeout=timeout)
File "/usr/local/lib/python2.7/dist-packages/elasticsearch/connection/http_urllib3.py", line 86, in perform_request
self._raise_error(response.status, raw_data)
File "/usr/local/lib/python2.7/dist-packages/elasticsearch/connection/base.py", line 102, in _raise_error
raise HTTP_EXCEPTIONS.get(status_code, TransportError)(status_code, error_message, additional_info)
elasticsearch.exceptions.NotFoundError: TransportError(404, u'{"exists":false}')

# Fixed code
def perform_request(self, method, url, params=None, body=None):
    """
    Perform the actual request. Retrieve a connection from the connection
    pool, pass all the information to it's perform_request method and
    return the data.

    If an exception was raised, mark the connection as failed and retry (up
    to `max_retries` times).

    If the operation was succesful and the connection used was previously
    marked as dead, mark it as live, resetting it's failure count.

    :arg method: HTTP method to use
    :arg url: absolute url (without host) to target
    :arg params: dictionary of query parameters, will be handed over to the
        underlying :class:`~elasticsearch.Connection` class for serialization
    :arg body: body of the request, will be serializes using serializer and
        passed to the connection
    """
    if body is not None:
        body = self.serializer.dumps(body)

        # some clients or environments don't support sending GET with body
        if method in ("HEAD", "GET") and self.send_get_body_as != "GET":
            # send it as post instead
            if self.send_get_body_as == "POST":
                method = "POST"

            # or as source parameter
            elif self.send_get_body_as == "source":
                if params is None:
                    params = {}
                params["source"] = body
                body = None

    if body is not None:
        try:
            body = body.encode("utf-8")
        except (UnicodeDecodeError, AttributeError):
            # bytes/str - no need to re-encode
            pass

    ignore = ()
    timeout = None
    if params:
        timeout = params.pop("request_timeout", None)
        ignore = params.pop("ignore", ())
        if isinstance(ignore, int):
            ignore = (ignore,)

    for attempt in range(self.max_retries + 1):
        connection = self.get_connection()

        try:
            status, headers, data = connection.perform_request(
                method, url, params, body, ignore=ignore, timeout=timeout
            )

        except TransportError as e:
            retry = False
            if isinstance(e, ConnectionTimeout):
                retry = self.retry_on_timeout
            elif isinstance(e, ConnectionError):
                retry = True
            elif e.status_code in self.retry_on_status:
                retry = True

            if retry:
                # only mark as dead if we are retrying
                self.mark_dead(connection)
                # raise exception on last retry
                if attempt == self.max_retries:
                    raise
            else:
                raise

        else:
            # connection didn't fail, confirm it's live status
            self.connection_pool.mark_live(connection)
            if data:
                data = self.deserializer.loads(data, headers.get("content-type"))
            return status, data