8

I'm running online predictions on google cloud machine learning API using the google api python client and a model hosted for me at google cloud. When I predict sending one image, the server, including all traffic, is taking about 40 seconds. When I send two images, after some time, I receive the message:

timeout: The read operation timed out

I would like to set the timeout to other value, but I didn't find how.

This is my code:

import base64
import io
import time
from PIL import Image    

from oauth2client.service_account import ServiceAccountCredentials
from googleapiclient import discovery

SCOPES = ['https://www.googleapis.com/auth/cloud-platform']
SERVICE_ACCOUNT_FILE = 'mycredentialsfile.json'

credentials = ServiceAccountCredentials.from_json_keyfile_name(
        SERVICE_ACCOUNT_FILE, scopes=SCOPES)

ml = discovery.build('ml', 'v1', credentials=credentials)

projectID = 'projects/{}'.format('projectID') + '/models/{}'.format('modelID')

width = 640
height = 480

instances = []

for image in ["image5.jpg", "image6.jpg"]:
    img = Image.open(image)
    img = img.resize((width, height), Image.ANTIALIAS)
    output_str = io.BytesIO()
    img.save(output_str, "JPEG")
    instance = {"b64": base64.b64encode(output_str.getvalue()).decode("utf-8") }
    output_str.close()
    instances.append(instance)  

input_json = {"instances": instances }

request = ml.projects().predict(body=input_json, name=projectID)

print("Starting prediction")
start_time = time.time()
response = request.execute()

print("%s seconds" % (time.time() - start_time))
Randolfo
  • 647
  • 1
  • 8
  • 18

4 Answers4

6

yes. I agree with Shohei's answer above. It took me a while to find this simple and elegant resolution. You only need to add the following to the code

import socket
timeout_in_sec = 60*3 # 3 minutes timeout limit
socket.setdefaulttimeout(timeout_in_sec)

# then you could create your ML service object as usually, and it will have the extended timeout limit.
ml_service = discovery.build('ml', 'v1')
Hui Zheng
  • 2,394
  • 1
  • 14
  • 18
5

I found a way researching samples from google api python client on github and trying same changes.

Using the httplib2 to authenticate you can set the timeout.

Following the modified code:

import base64
import io
import time
from PIL import Image

# Need: pip install google-api-python-client

import httplib2
from oauth2client.service_account import ServiceAccountCredentials
from googleapiclient import discovery

SCOPES = ['https://www.googleapis.com/auth/cloud-platform']
# API & Services -> Credentials -> Create Credential -> service account key
SERVICE_ACCOUNT_FILE = 'mycredentialsfile.json'

credentials = ServiceAccountCredentials.from_json_keyfile_name(
        SERVICE_ACCOUNT_FILE, scopes=SCOPES)

http = httplib2.Http(timeout=200)
http = credentials.authorize(http)

ml = discovery.build('ml', 'v1', http=http)

projectID = 'projects/{}'.format('projectID ') + '/models/{}'.format('modelID')

width = 640
height = 480

instances = []

for image in ["image5.jpg", "image6.jpg"]:
    img = Image.open(image)
    img = img.resize((width, height), Image.ANTIALIAS)
    output_str = io.BytesIO()
    img.save(output_str, "JPEG")
    instance = {"b64": base64.b64encode(output_str.getvalue()).decode("utf-8") }
    output_str.close()
    instances.append(instance)  

input_json = {"instances": instances }

request = ml.projects().predict(body=input_json, name=projectID)

print("Starting prediction")
start_time = time.time()
response = request.execute()

print("%s seconds" % (time.time() - start_time))

I think with a few modifications you can use this to set timeout to almost any google cloud API in python client.

I hope this helps.

Randolfo
  • 647
  • 1
  • 8
  • 18
  • Unfortunately ran into: ```googleapiclient.errors.HttpError: ```, will keep looking around – adammenges Mar 12 '18 at 20:07
  • Hi @adammenges ! Looks like you have a problem with your credentials, do you create your own "mycredentialsfile.json file" in the google cloud platform console? If no, probably this is the problem reason. If yes, the problem could be the type of your credentials file. – Randolfo Mar 14 '18 at 10:42
  • Hi, there is a easier way to handle this. you could just use `socket.setdefaulttimeout(timeout_in_sec)`. Other answers below in this thread give more details. – Hui Zheng May 15 '19 at 16:30
4

You have already solved the problem, but I found the other way to do this.

import socket    
socket.setdefaulttimeout(150)

If call discovery.build without http, http client is instantiated by build_http in build method.

https://googleapis.github.io/google-api-python-client/docs/epy/googleapiclient.http-pysrc.html#build_http

As you can see here, build_http create a http client instance with timeout if it is set before creating http client.

So all you have to do is setting this value by socket.setdefaulttimeout :)

  • yes. I agree with Shohei, It took me a while to find this simple and elegant resolution. All you need is – Hui Zheng May 15 '19 at 16:23
1

I don't agree with the accepted answer as this can lead to limitations when using the api. I found the corresponding code here: https://github.com/googleapis/google-api-python-client/blob/main/googleapiclient/http.py#L1933-L1962

This is what happens inside:

def build_http():
"""Builds httplib2.Http object

Returns:
A httplib2.Http object, which is used to make http requests, and which has timeout set by default.
To override default timeout call

  socket.setdefaulttimeout(timeout_in_sec)

before interacting with this method.
"""
if socket.getdefaulttimeout() is not None:
    http_timeout = socket.getdefaulttimeout()
else:
    http_timeout = DEFAULT_HTTP_TIMEOUT_SEC
http = httplib2.Http(timeout=http_timeout)
# 308's are used by several Google APIs (Drive, YouTube)
# for Resumable Uploads rather than Permanent Redirects.
# This asks httplib2 to exclude 308s from the status codes
# it treats as redirects
try:
    http.redirect_codes = http.redirect_codes - {308}
except AttributeError:
    # Apache Beam tests depend on this library and cannot
    # currently upgrade their httplib2 version
    # http.redirect_codes does not exist in previous versions
    # of httplib2, so pass
    pass

return http

As you can see there are further configurations added to the http instance in the try block. So I would recommend @Shohei Miyashita approach with the socket.

That being said I do not think that this is a good solution either because this is a low level setting meaning that it could apply to other http clients. What I would recommend is setting it back to its default value after the API client has been instantiated:

import socket    
socket.setdefaulttimeout(150)
# instantiate API client here    
sock = socket.socket()
sock.settimeout(None)
Sylver11
  • 129
  • 1
  • 8