Skip to content

Commit 9cf1626

Browse files
use deadline & test pubsub/index
1 parent b2d163b commit 9cf1626

File tree

4 files changed

+165
-97
lines changed

4 files changed

+165
-97
lines changed

lib/common/grpc-service.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ nodeutil.inherits(GrpcService, Service);
158158
* @param {object} protoOpts - The proto options.
159159
* @param {string} protoOpts.service - The service name.
160160
* @param {string} protoOpts.method - The method name.
161+
* @param {number=} protoOpts.timeout - After how many milliseconds should the
162+
* request cancel.
161163
* @param {object} reqOpts - The request options.
162164
* @param {function=} callback - The callback function.
163165
*/
@@ -185,11 +187,22 @@ GrpcService.prototype.request = function(protoOpts, reqOpts, callback) {
185187
return;
186188
}
187189

190+
var grpcOpts = {};
191+
192+
if (is.number(protoOpts.timeout)) {
193+
grpcOpts.deadline = new Date(Date.now() + protoOpts.timeout);
194+
}
195+
188196
var service = new proto[protoOpts.service](
189197
this.baseUrl,
190198
this.grpcCredentials
191199
);
192200

201+
// snakeize and camelize are used to transform camelCase request options to
202+
// snake_case. This is what ProtoBuf.js (via gRPC) expects. Similarly, the
203+
// response is in snake_case, which is why we use camelize to return it to
204+
// camelCase. An option will be added to gRPC to allow us to skip this step:
205+
// https://github.com/GoogleCloudPlatform/gcloud-node/pull/1070#discussion_r51285492
193206
service[protoOpts.method](snakeize(reqOpts), function(err, resp) {
194207
if (err) {
195208
if (HTTP_ERROR_CODE_MAP[err.code]) {
@@ -210,8 +223,9 @@ GrpcService.prototype.request = function(protoOpts, reqOpts, callback) {
210223
for (var prop in data) {
211224
var value = data[prop];
212225

213-
// @todo - Set preference on gRPC deserializeCls method to not give
214-
// raw buffers.
226+
// An option will be added to gRPC to expose a setting which will
227+
// replace this function (convertBuffers).
228+
// https://github.com/GoogleCloudPlatform/gcloud-node/pull/1070#discussion_r51285492
215229
if (is.object(value) && value.length) {
216230
data[prop] = new Buffer(value).toString('base64');
217231
} else if (is.object(value)) {
@@ -224,7 +238,7 @@ GrpcService.prototype.request = function(protoOpts, reqOpts, callback) {
224238
}
225239

226240
callback(null, convertBuffers(camelize(resp)));
227-
});
241+
}, null, grpcOpts);
228242
};
229243

230244
module.exports = GrpcService;

lib/pubsub/index.js

+11-7
Original file line numberDiff line numberDiff line change
@@ -359,9 +359,9 @@ PubSub.prototype.getTopics = function(query, callback) {
359359
method: 'listTopics'
360360
};
361361

362-
var reqOpts = extend(query, {
362+
var reqOpts = extend({
363363
project: 'projects/' + this.projectId
364-
});
364+
}, query);
365365

366366
this.request(protoOpts, reqOpts, function(err, result) {
367367
if (err) {
@@ -416,6 +416,9 @@ PubSub.prototype.getTopics = function(query, callback) {
416416
* reuse it. The options of the existing subscription are not changed. If
417417
* false, attempting to create a subscription that already exists will fail.
418418
* (default: false)
419+
* @param {number} options.timeout - Set a maximum amount of time in
420+
* milliseconds on an HTTP request to pull new messages to wait for a
421+
* response before the connection is broken.
419422
* @param {function} callback - The callback function.
420423
* @param {?error} callback.err - An error returned while making this request
421424
* @param {module:pubsub/subscription} callback.subscription - The subscription.
@@ -451,8 +454,6 @@ PubSub.prototype.getTopics = function(query, callback) {
451454
* pubsub.subscribe(topic, name, function(err, subscription, apiResponse) {});
452455
*/
453456
PubSub.prototype.subscribe = function(topic, subName, options, callback) {
454-
var self = this;
455-
456457
if (!is.string(topic) && !(topic instanceof Topic)) {
457458
throw new Error('A Topic is required for a new subscription.');
458459
}
@@ -470,29 +471,32 @@ PubSub.prototype.subscribe = function(topic, subName, options, callback) {
470471
topic = this.topic(topic);
471472
}
472473

474+
var subscription = this.subscription(subName, options);
475+
473476
var protoOpts = {
474477
service: 'Subscriber',
475-
method: 'createSubscription'
478+
method: 'createSubscription',
479+
timeout: options.timeout
476480
};
477481

478482
var reqOpts = extend(true, {}, options, {
479483
topic: topic.name,
480-
name: this.subscription(subName).name
484+
name: subscription.name
481485
});
482486

483487
delete reqOpts.autoAck;
484488
delete reqOpts.encoding;
485489
delete reqOpts.interval;
486490
delete reqOpts.maxInProgress;
487491
delete reqOpts.reuseExisting;
492+
delete reqOpts.timeout;
488493

489494
this.request(protoOpts, reqOpts, function(err, resp) {
490495
if (err && !(err.code === 409 && options.reuseExisting)) {
491496
callback(err, null, resp);
492497
return;
493498
}
494499

495-
var subscription = self.subscription(subName, options);
496500
callback(null, subscription, resp);
497501
});
498502
};

lib/pubsub/subscription.js

+24-3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ var util = require('../common/util.js');
5757
* @param {string} options.name - Name of the subscription.
5858
* @param {number} options.maxInProgress - Maximum messages to consume
5959
* simultaneously.
60+
* @param {number} options.timeout - Set a maximum amount of time in
61+
* milliseconds on an HTTP request to pull new messages to wait for a
62+
* response before the connection is broken. (default: 90000)
6063
*/
6164
/**
6265
* A Subscription object will give you access to your Google Cloud Pub/Sub
@@ -250,6 +253,16 @@ function Subscription(pubsub, options) {
250253
this.messageListeners = 0;
251254
this.paused = false;
252255

256+
if (is.number(options.timeout)) {
257+
this.timeout = options.timeout;
258+
} else {
259+
// The default timeout used in gcloud-node is 60s, but a pull request times
260+
// out around 90 seconds. Allow an extra couple of seconds to give the API a
261+
// chance to respond on its own before terminating the connection.
262+
var PUBSUB_API_TIMEOUT = 90000;
263+
this.timeout = PUBSUB_API_TIMEOUT + 2000;
264+
}
265+
253266
/**
254267
* [IAM (Identity and Access Management)](https://cloud.google.com/pubsub/access_control)
255268
* allows you to set permissions on invidual resources and offers a wider
@@ -511,7 +524,8 @@ Subscription.prototype.pull = function(options, callback) {
511524

512525
var protoOpts = {
513526
service: 'Subscriber',
514-
method: 'pull'
527+
method: 'pull',
528+
timeout: this.timeout
515529
};
516530

517531
var reqOpts = {
@@ -522,8 +536,15 @@ Subscription.prototype.pull = function(options, callback) {
522536

523537
this.request(protoOpts, reqOpts, function(err, response) {
524538
if (err) {
525-
callback(err, null, response);
526-
return;
539+
if (err.code === 504) {
540+
// Simulate a server timeout where no messages were received.
541+
response = {
542+
receivedMessages: []
543+
};
544+
} else {
545+
callback(err, null, response);
546+
return;
547+
}
527548
}
528549

529550
var messages = arrify(response.receivedMessages)

0 commit comments

Comments
 (0)