Repository navigation
PubSub: Subscriber Client stops acking messages after several seconds #4274
Copy link
Copy link
Closed
Labels
api: pubsubIssues related to the Pub/Sub API.Issues related to the Pub/Sub API.priority: p1Important issue which blocks shipping the next release. Will be fixed prior to next release.Important issue which blocks shipping the next release. Will be fixed prior to next release.
Description
-
Specify the API at the beginning of the title (for example, "BigQuery: ...")
General, Core, and Other are also allowed as types -
OS type and version
python:3-alpine docker image running in GKE -
Python version and virtual environment information
python --version -
google-cloud-python version
pip show google-cloud,pip show google-<service>orpip freeze
google-cloud-pubsub==0.28.4
grpcio==1.6.3 -
Stacktrace if available
-
Steps to reproduce
Run example code, watch stackdriver metrics for acks vs modify_ack_deadline -
Code example
import os
import sys
import logging
import traceback
import json
import time
import argparse
import grequests
import grpc
from item_store import ItemStore
from google.cloud.pubsub_v1.subscriber.policy.thread import Policy
from google.cloud import pubsub_v1
requests = ItemStore()
class OurPolicy(Policy):
"""
We occasionally see errors that google code doesn't
recover from, set a flag that let's the outer thread respond
by restarting the client.
grpc._channel._Rendezvous: <_Rendezvous of RPC that terminated
with (StatusCode.UNAVAILABLE, OS Error)>
"""
_exception_caught = None
def __init__(self, *args, **kws):
return super(OurPolicy, self).__init__(*args, **kws)
def on_exception(self, exc):
# If this is DEADLINE_EXCEEDED, then we want to retry by returning
# None instead of raise-ing
deadline_exceeded = grpc.StatusCode.DEADLINE_EXCEEDED
code_value = getattr(exc, 'code', lambda: None)()
if code_value == deadline_exceeded:
return
OurPolicy._exception_caught = exc
# will just raise exc
return super(OurPolicy, self).on_exception(exc)
class InvalidSchemaException(Exception):
pass
def log_unhandled_exception(type, value, traceback):
logger.error(type, value, traceback)
sys.excepthook = log_unhandled_exception
def send_to_data_insertion(message):
requests.add(grequests.post('http://%s:8080/url_here=%s' % (
address, token),
data=message.data.decode('utf-8')))
############### Subscriber logic here ####################
def receive_messages(project, subscription_name):
subscriber = pubsub_v1.SubscriberClient(policy_class=OurPolicy)
subscription_path = subscriber.subscription_path(
project, subscription_name)
def callback(message):
message.ack()
try:
send_to_data_insertion(message)
except InvalidSchemaException as e:
return
except Exception as e:
return
while live_forever:
if subscriber is None:
logger.warning('Starting pubsub subscriber client')
subscriber = pubsub_v1.SubscriberClient(policy_class=OurPolicy)
subscriber.subscribe(subscription_path).open(callback=callback)
try:
while True:
grequests.map(requests.getAll())
time.sleep(sleep_interval)
if OurPolicy._exception_caught:
exc = OurPolicy._exception_caught
OurPolicy._exception_caught = None
raise exc
except KeyboardInterrupt:
break
except Exception as e:
subscriber = None
# otherwise, sleep for one interval and exit
time.sleep(sleep_interval)
#####################################################
if __name__ == "__main__":
parser.add_argument('project', help='Google cloud project ID')
parser.add_argument('subscription', help="Google cloud subscription name")
args = parser.parse_args()
receive_messages(args.project, args.subscription)Using GitHub flavored markdown can help make your request clearer.
See: https://guides.github.com/features/mastering-markdown/
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
api: pubsubIssues related to the Pub/Sub API.Issues related to the Pub/Sub API.priority: p1Important issue which blocks shipping the next release. Will be fixed prior to next release.Important issue which blocks shipping the next release. Will be fixed prior to next release.

Activity
markcwhitfield commentedon Oct 27, 2017
Don't have any solutions for you, but I'm also trying out the new API and seeing similar issues.
I'm also seeing
StatusCode.UNAVAILABLEkilling the consumer thread. My approach was just to let the policy itself retry by returningNoneinon_exception, same asDEADLINE_EXCEEDED. I don't understand why this or something similar wouldn't be the case in the default policy, as occasionalUNAVAILABLEresponses seem expected and inevitable, and the default policy's approach is to let the thread die without any way to detect or recover it in the main thread.In any case, that modification of
on_exceptiondefinitely helps. I am still seeing a very large number of repeat messages, though I haven't done thorough enough testing to even pin it down to the library or my own code.EDIT: I see there are several similar issues already opened. Between this, #4186, and #3886 upgrading Pub/Sub has been a real pain, but the old API holds back every other client :/
lukesneeringer commentedon Oct 30, 2017
I think this issue is a duplicate of #4238, but leaving open just in case (and because this is a really useful bug report).
I think I have a handle on the root cause of this and am working on it this week. I actually got a fix in for the exception handling issue last week, but we have not released it to PyPI yet.
[-]PubSub: Pubsub Python Client stops acking messages after several seconds[/-][+]Subscriber Client stops acking messages after several seconds[/+]anorth2 commentedon Oct 30, 2017
More info on this:
What it appears to do is drop the ack rate to between 0/s and 10/s while StreamingPullOperations climb into the thousands. It also has thousands of ModifyAckDeadline events. So it seems to keep repulling (?) the same events and extending their deadline so they don't get resent to other subscribers.
eoltean commentedon Nov 1, 2017
Any updates on this?
[-]Subscriber Client stops acking messages after several seconds[/-][+]Pubsub: Subscriber Client stops acking messages after several seconds[/+][-]Pubsub: Subscriber Client stops acking messages after several seconds[/-][+]PubSub: Subscriber Client stops acking messages after several seconds[/+]lukesneeringer commentedon Nov 3, 2017
@eoltean Still working on it. :-/
lukesneeringer commentedon Nov 3, 2017
Let me make sure I understand this correctly. You are saying that your acks cease going through (except in small amounts) while the modacks do?
anorth2 commentedon Nov 3, 2017
Unless I misunderstand the graph, then yes that's what is going on. These are all the same time period. The slow burndown in unacked messages is the python client. When the unacked messages dropped suddenly, that is when we switched to the Go client for testing.
For most of the period of these graphs the ack rate is from 0 to 10/s with a few random spikes where it hit several hundred per second before dropping.
Ignore from-data-insertion-svc, that is another Topic/Sub using the same client code
Zoomed (two time periods, one with 0 acks, one with acks)


adamlofts commentedon Nov 6, 2017
Could someone recommend a version of the pubsub client which doesn't have this bug? I've just started using pubsub and this bug makes it unusable. Thanks.
Note for those debugging this issue: I only see this problem on GKE and not when running locally.
eoltean commentedon Nov 6, 2017
This is happening both locally and on GKE for us
2 remaining items
lukesneeringer commentedon Nov 8, 2017
(Status update: Work still in progress trying to fix this.)
anorth2 commentedon Nov 14, 2017
Any updates on this? Our team is trying to decide if the production version of our project can rely on the Python client or if we need to switch to Go/Java.
eoltean commentedon Nov 30, 2017
@lukesneeringer I see a couple of the other issues are being resolved do these effect this issue at all? Is there any other updates on this its been over 3 weeks since any update.
dhermes commentedon Nov 30, 2017
@eoltean I'll try to get to this soon. Hopefully it'll be reproducible (since @anorth2 provided so much code, it seems there are plenty of details).
AndreCimander commentedon Dec 5, 2017
We might be running into a related problem, we are currently rewriting the psq package to suit our needs and I have trouble with ack'ing messages after an exception occurred. Is the message somehow auto-nack'ed after an exception is thrown?
If I start a multi-process worker I can watch the task getting redelivered to the other processes just to also fail.
theacodes commentedon Dec 5, 2017
skycoop commentedon Dec 6, 2017
Any updates on this?
dmontag commentedon Dec 11, 2017
I might be seeing this issue as well. I have a single subscriber listening to infrequent changes in a GCS bucket using a PubSub topic. My subscriber callback looks like this:
Old messages keep getting delivered over and over, and eventually I think the un-ack()'d backlog blocks new messages from being processed.

I'm using pubsub 0.29.3, cloud 0.31.0, core 0.28.0, grpcio 1.7.3 and Python 3.6.3.
Edit: I rewrote my script using google-cloud-pubsub==0.27.0 as mentioned by @anorth2, and it works correctly. I'll also try the newly released 0.29.4 to see how it works.
Edit 2: 0.29.4 seemed to handle message pulling a bit better but not perfectly (once it hung on receiving messages, but they seemed to be acknowledged when I restarted the app). However, it exhibits the separate behavior of eventually consuming 100% CPU that I reported in #4563, so that forced me to use 0.27.0 anyway.
anorth2 commentedon Dec 14, 2017
To provide an update, we're currently on 0.29.4 and seeing the same (if not worse) behavior as before. Hitting 14 GB of memory usage after about 10 minutes, only acking at around 5-10/s, 2000-7000+ streamingpulloperations/second:
dhermes commentedon Dec 14, 2017
@anorth2, there is a spinlock bug in gRPC that I think may be the culprit. A PR (grpc/grpc#13665) is out as a potential fix and I am going to install
grpciofrom source based on this PR to see if it resolves the issue.Can you run a
straceon the impactedpthread? For example, when25626was my parent process:I identified
5033as the runawaypthreadand attaching to it shows thePOLLIN/POLLHUPissue:The current example I have to reproduce the CPU spike usually takes an hour to appear, I'd love it if I could get a spike within 10 minutes so I could run a reproducible case more often. Ping me on Hangouts and maybe we can figure something out?
dhermes commentedon Dec 14, 2017
I have backported that fix to the
v1.7.3tag and will be installing from source and running it now to see if it solves the spinlock issue.dhermes commentedon Dec 14, 2017
So I just ran my "do-nothing" reproducible case (thanks to @dmontag) that reliably thrashes the CPU after 65 minutes. I can confirm that after installing a custom
grpciowith grpc/grpc#13665, the thrashing goes away!I have created a
manylinux64-bit wheel that includes the backport: https://github.com/dhermes/google-cloud-pubsub-performance/blob/master/grpcio-1.7.4.dev1-cp36-cp36m-manylinux1_x86_64.whlFeel free to install but "buyer beware" this isn't a wheel endorsed by the
grpciofolks. The wheel was created via:All of these commands will work on your target machine except for
auditwheel, but if you build a custom wheel on your target machine (i.e. a wheel not intended for "general distribution") then you don't need to useauditwheel. Also note you'll need to change the relevant Python path from/opt/python/cp36-cp36m/bin/python3.6.dhermes commentedon Dec 15, 2017
@anorth2 Would you mind trying with the patched
grpcio==1.7.4.dev1?dhermes commentedon Dec 15, 2017
I'm closing this issue in favor of #4600. The original report "PubSub: Subscriber Client stops acking messages after several seconds" has been resolved by the addition of
Consumer.pause()andConsumer.resume()in #4558.@anorth2 Can we move future discussion there? I'll move my last pending question there for "completeness".