Migrating to a version of TChannel with breaking changes? This guide documents what broke and how to safely migrate to newer versions.
tchannel.thrift.registerreturns the original function as-is instead of the wrapped version. This allows writing unit tests that call the handler function directly.Previously, if you used the
tchannel.thrift.registerdecorator to register a Thrift endpoint and then called that function directly from a test, it would return aResponseobject if the call succeeded or failed with an expected exception (defined in the Thrift IDL). For example,# service KeyValue { # string getValue(1: string key) # throws (1: ValidationError invalid) # } @tchannel.thrift.register(kv.KeyValue) def getValue(request): key = request.body.key if key == 'invalid': raise kv.ValidationError() result = # ... return result response = getValue(make_request(key='invalid')) if response.body.invalid: # ... else: result = response.body.success
With 0.21, we have changed
tchannel.thrift.registerto return the unmodified function so that you can call it directly and it will behave as expected.@tchannel.thrift.register(kv.KeyValue) def getValue(request): # ... try: result = getValue(make_request(key='invalid')) except kv.ValidationError: # ...
- No breaking changes.
- No breaking changes.
request.headersin a JSON handler is no longer a JSON blob. Instead it is a dictionary mapping strings to strings. This matches the Thrift implementation. If your headers include richer types like lists or ints, you'll need to coordinate with your callers to no longer pass headers as JSON blobs. The same applies to JSON requsts; rich headers will now fail to serialize.If you were accessing
request_clsorresponse_clsdirectly from a service method in a module generated bytchannel.thrift.load, you can no longer do that. Therequest_clsandresponse_clsattributes are internal details of the implementation and have been changed to protected. You should only ever use the service method directly.Before:
my_service.doSomething.request_cls(..)
After:
my_service.doSomething(..)
Note that
request_clsgives you just an object containing the method arguments. It does not include any of the other information needed to make the request. So if you were using it to make requests, it wouldn't have worked anyway.
- No breaking changes.
tchannel.TChannel.registerno longer mimickstchannel.tornado.TChannel.register, instead it exposes the new server API like so:Before:
from tchannel.tornado import TChannel tchannel = TChannel('my-service-name') @tchannel.register('endpoint', 'json') def endpoint(request, response, proxy): response.write({'resp': 'body'})
After:
from tchannel import TChannel tchannel = TChannel('my-service-name') @tchannel.json.register def endpoint(request): return {'resp': 'body'} # Or, if you need to return headers with your response: from tchannel import Response return Response({'resp': 'body'}, {'header': 'foo'})
TChannelSyncClienthas been replaced withtchannel.sync.TChannel. This new synchronous client has been significantly re-worked to more closely match the asynchronousTChannelAPI.tchannel.sync.thrift.client_forhas been removed andtchannel.thrift_request_buildershould be used instead (tchannel.thrift.client_forstill exists for backwards compatibility but is not recommended). This new API allows specifying headers, timeouts, and retry behavior with Thrift requests.Before:
from tchannel.sync import TChannelSyncClient from tchannel.sync.thrift import client_for from generated.thrift.code import MyThriftService tchannel_thrift_client = client_for('foo', MyThriftService) tchannel = TChannelSyncClient(name='bar') future = tchannel_thrift_client.someMethod(...) result = future.result()
After:
from tchannel import thrift_request_builder from tchannel.sync import TChannel from tchannel.retry import CONNECTION_ERROR_AND_TIMEOUT from generated.thrift.code import MyThriftService tchannel_thrift_client = thrift_request_builder( service='foo', thrift_module=MyThriftService, ) tchannel = TChannel(name='bar') future = tchannel.thrift( tchannel_thrift_client.someMethod(...) headers={'foo': 'bar'}, retry_on=CONNECTION_ERROR_AND_TIMEOUT, timeout=1000, ) result = future.result()
from tchannel.tornado import TChannelis deprecated.Removed
retry_delayoption fromtchannel.tornado.peer.PeerClientOperation.sendmethod.Before:
tchannel.tornado.TChannel.request.send(retry_delay=300)After: no more
retry_delayintchannel.tornado.TChannel.request.send()If you were catching
ProtocolErroryou will need to catch a more specific type, such asTimeoutError,BadRequestError,NetworkError,UnhealthyError, orUnexpectedError.If you were catching
AdvertiseError, it has been replaced byTimeoutError.If you were catching
BadRequest, it may have been masking checksum errors and fatal streaming errors. These are now raised asFatalProtocolError, but in practice should not need to be handled when interacting with a well-behaved TChannel implementation.TChannelApplicationErrorwas unused and removed.Three error types have been introduced to simplify retry handling:
NotRetryableError(for requests should never be retried),RetryableError(for requests that are always safe to retry), andMaybeRetryableError(for requests that are safe to retry on idempotent endpoints).
- No breaking changes.
- No breaking changes.
- No breaking changes.
- Removed
print_arg. Userequest.get_body()instead.
Renamed
tchannel.tornado.TChannel.advertiseargumentroutertorouters. Since this is a required arg and the first positional arg, only clients who are using as kwarg will break.Before:
tchannel.advertise(router=['localhost:21300'])After:
tchannel.advertise(routers=['localhost:21300'])