Comparing version 3.9.3 to 3.9.4
@@ -1,3 +0,28 @@ | ||
# v3.8.1 | ||
# v3.9.3 | ||
- Check whether requests time out while waiting for connection identification. | ||
# v3.9.2 | ||
- Refactor: move updatePeer and drainPeer up to TChannel level. | ||
# v3.9.1 | ||
- Add option to pass pre-compiled thrift spec to TChannelAsThrift. | ||
# v3.9.0 | ||
- Support per-client retry budget to avoid excessive retries overwhelming server. | ||
- Fixup benchmarks Makefile to work out of box. | ||
# v3.8.4, v3.8.5 (bugfix) | ||
- Improve the connection backoff logic. | ||
# v3.8.3 | ||
- Adds the request object as an argument to `isBusy(req)`. | ||
# v3.8.1, v3.8.2 (bugfix) | ||
- Adds an `isUnhealthyError` method to TChannel for use in throttling (client | ||
@@ -4,0 +29,0 @@ side rate limiting). |
@@ -1143,5 +1143,9 @@ // Copyright (c) 2016 Uber Technologies, Inc. | ||
var connection = self.serverConnections[connectionKeys[index]]; | ||
connection.ops.sanitySweep(function opsSweepDone() { | ||
if (!connection || !connection.ops) { | ||
setImmediate(deferNextConn); | ||
}); | ||
} else { | ||
connection.ops.sanitySweep(function opsSweepDone() { | ||
setImmediate(deferNextConn); | ||
}); | ||
} | ||
@@ -1148,0 +1152,0 @@ function deferNextConn() { |
@@ -5,3 +5,3 @@ { | ||
"author": "mranney@uber.com", | ||
"version": "3.9.3", | ||
"version": "3.9.4", | ||
"scripts": { | ||
@@ -8,0 +8,0 @@ "lint": "eslint $(git ls-files | grep '.js$')", |
@@ -83,2 +83,19 @@ // Copyright (c) 2016 Uber Technologies, Inc. | ||
function RequestOperation(req, timeout, peer, waitForIdentifiedSlot) { | ||
this.req = req; | ||
this.timeout = timeout; | ||
this.peer = peer; | ||
this.waitForIdentifiedSlot = | ||
typeof waitForIdentifiedSlot === 'number' ? | ||
waitForIdentifiedSlot : -1; | ||
} | ||
RequestOperation.prototype.onTimeout = function onTimeout(now) { | ||
if (this.waitForIdentifiedSlot !== -1) { | ||
this.peer.stopWaitingForIdentified(this.waitForIdentifiedSlot); | ||
} | ||
this.req.checkTimeout(); | ||
}; | ||
TChannelRequest.prototype.type = 'tchannel.request'; | ||
@@ -308,5 +325,21 @@ | ||
peer.waitForIdentified(onIdentified); | ||
var conn = peer.getInConnection(true); | ||
var reqTimeout; | ||
if (conn && conn.remoteName && !conn.closing) { | ||
self.onIdentified(peer); | ||
} else { | ||
var waitForIdentifiedSlot = peer.waitForIdentified(onIdentified); | ||
var now = self.channel.timers.now(); | ||
self.elapsed = now - self.start; | ||
var timeout = self.timeout - self.elapsed; | ||
var reqOp = new RequestOperation( | ||
self, timeout, peer, waitForIdentifiedSlot | ||
); | ||
reqTimeout = self.channel.timeHeap.update(reqOp, now); | ||
} | ||
function onIdentified(err) { | ||
reqTimeout.cancel(); | ||
if (err) { | ||
@@ -313,0 +346,0 @@ /* emulate outReq failure */ |
@@ -438,2 +438,123 @@ // Copyright (c) 2016 Uber Technologies, Inc. | ||
allocCluster.test('requests will timeout exactly with slow conn', { | ||
numPeers: 3 | ||
}, function t(cluster, assert) { | ||
var one = cluster.channels[0]; | ||
var two = cluster.channels[1]; | ||
var three = cluster.channels[2]; | ||
cluster.logger.whitelist( | ||
'info', 'error for timed out outgoing response' | ||
); | ||
cluster.logger.whitelist( | ||
'info', 'ignoring outresponse.send on a closed connection' | ||
); | ||
cluster.logger.whitelist( | ||
'info', 'OutResponse.send() after inreq timed out' | ||
); | ||
cluster.logger.whitelist( | ||
'warn', ' Got a connection error' | ||
); | ||
cluster.logger.whitelist( | ||
'warn', 'destroying due to init timeout' | ||
); | ||
cluster.logger.whitelist( | ||
'warn', 'resetting connection' | ||
); | ||
cluster.logger.whitelist('warn', 'Got a connection error'); | ||
// server | ||
var sub = one.makeSubChannel({ | ||
serviceName: 'server' | ||
}); | ||
sub.register('/normal-proxy', normalProxy); | ||
one.connectionEvent.on(function onConnectionEvent(conn) { | ||
var socket = conn.socket; | ||
socket.pause(); | ||
setTimeout(function delaySocket() { | ||
socket.resume(); | ||
}, 1000); | ||
}); | ||
// client | ||
var twoSub = two.makeSubChannel({ | ||
serviceName: 'server', | ||
peers: [one.hostPort] | ||
}); | ||
var start = 0; | ||
// make normal request | ||
twoSub | ||
.request({ | ||
serviceName: 'server', | ||
hasNoParent: true, | ||
headers: { | ||
'as': 'raw', | ||
cn: 'wat' | ||
}, | ||
timeout: 2000 | ||
}) | ||
.send('/normal-proxy', 'h', 'b', onResp); | ||
function onResp(err, res, arg2, arg3) { | ||
assert.ifError(err); | ||
assert.equal(String(arg2), 'h'); | ||
assert.equal(String(arg3), 'b'); | ||
doSecond(); | ||
} | ||
function doSecond() { | ||
// another client | ||
var threeSub = three.makeSubChannel({ | ||
serviceName: 'server', | ||
peers: [one.hostPort] | ||
}); | ||
start = Date.now(); | ||
// slow request | ||
threeSub | ||
.request({ | ||
serviceName: 'server', | ||
hasNoParent: true, | ||
headers: { | ||
'as': 'raw', | ||
cn: 'wat' | ||
}, | ||
timeout: 450 | ||
}) | ||
.send('/normal-proxy', 'h', 'b', onTimeout); | ||
} | ||
function onTimeout(err, res, arg2, arg3) { | ||
assert.ok( | ||
(err && err.type) === 'tchannel.request.timeout', | ||
'expected timeout error' | ||
); | ||
var delta = Date.now() - start; | ||
if (typeof err.elapsed === 'number') { | ||
assert.ok(err.elapsed < 600, 'expected timeout within 600ms'); | ||
} | ||
assert.ok(delta < 600, 'expected timeout within 600ms'); | ||
if (delta >= 600) { | ||
console.log('d', delta); | ||
} | ||
setTimeout(function delay() { | ||
cluster.assertEmptyState(assert); | ||
assert.end(); | ||
}, 500); | ||
} | ||
function normalProxy(req, res, arg2, arg3) { | ||
res.headers.as = 'raw'; | ||
res.sendOk(arg2, arg3); | ||
} | ||
}); | ||
allocCluster.test('requests can succeed after timeout per attempt', { | ||
@@ -440,0 +561,0 @@ numPeers: 3, |
1591688
42050