class MethodBuilder extends BaseMethodBuilder[MethodBuilder]
MethodBuilder
is a collection of APIs for client configuration at
a higher level than the Finagle 6 APIs while improving upon the deprecated
ClientBuilder. MethodBuilder
provides:
- Logical success rate metrics.
- Retries based on application-level requests and responses (e.g. a code in the Thrift response).
- Configuration of per-attempt and total timeouts.
All of these can be customized per method (or endpoint) while sharing a single
underlying Finagle client. Concretely, a single service might offer both
getOneTweet
as well as deleteTweets
, whilst each having
wildly different characteristics. The get is idempotent and has a tight latency
distribution while the delete is not idempotent and has a wide latency
distribution. If users want different configurations, without MethodBuilder
they must create separate Finagle clients for each grouping. While long-lived
clients in Finagle are not expensive, they are not free. They create
duplicate metrics and waste heap, file descriptors, and CPU.
Example
Given an example IDL:
exception AnException { 1: i32 errorCode } service SomeService { i32 TheMethod( 1: i32 input ) throws ( 1: AnException ex1, ) }
This gives you a Service
that has timeouts and retries on
AnException
when the errorCode
is 0
:
import com.twitter.conversions.DurationOps._ import com.twitter.finagle.ThriftMux import com.twitter.finagle.service.{ReqRep, ResponseClass} import com.twitter.util.Throw val client: ThriftMux.Client = ??? val svc: Service[TheMethod.Args, TheMethod.SuccessType] = client.methodBuilder("inet!example.com:5555") .withTimeoutPerRequest(50.milliseconds) .withTimeoutTotal(100.milliseconds) .withRetryForClassifier { case ReqRep(_, Throw(AnException(errCode))) if errCode == 0 => ResponseClass.RetryableFailure } .newServiceIface("the_method") .theMethod
Timeouts
Defaults to using the StackClient's configuration.
An example of setting a per-request timeout of 50 milliseconds and a total timeout of 100 milliseconds:
import com.twitter.conversions.DurationOps._ import com.twitter.finagle.thriftmux.MethodBuilder val builder: MethodBuilder = ??? builder .withTimeoutPerRequest(50.milliseconds) .withTimeoutTotal(100.milliseconds)
Retries
Retries are intended to help clients improve success rate by trying failed requests additional times. Care must be taken by developers to only retry when it is known to be safe to issue the request multiple times. This is because the client cannot always be sure what the backend service has done. An example of a request that is safe to retry would be a read-only request.
Defaults to using the client's ResponseClassifier to retry failures marked as retryable. See withRetryForClassifier for details.
An example of configuring classifiers for ChannelClosed and Timeout exceptions:
import com.twitter.finagle.service.ResponseClassifier._ import com.twitter.finagle.thriftmux.MethodBuilder val builder: MethodBuilder = ??? builder .withRetryForClassifier(RetryOnChannelClosed.orElse(RetryOnTimeout))
A com.twitter.finagle.service.RetryBudget is used to prevent retries from overwhelming
the backend service. The budget is shared across clients created from
an initial MethodBuilder
. As such, even if the retry rules
deem the request retryable, it may not be retried if there is insufficient
budget.
Finagle will automatically retry failures that are known to be safe to retry via com.twitter.finagle.service.RequeueFilter. This includes WriteExceptions and retryable nacks. As these should have already been retried, we avoid retrying them again by ignoring them at this layer.
Additional information regarding retries can be found in the user guide.
The classifier is also used to determine the logical success metrics of the method. Logical here means after any retries are run. For example should a request result in retryable failure on the first attempt, but succeed upon retry, this is exposed through metrics as a success. Logical success rate metrics are scoped to "clnt/your_client_label/method_name/logical" and get "success" and "requests" counters along with a "request_latency_ms" stat.
Unsuccessful requests are logged at com.twitter.logging.Level.DEBUG
level.
Further details, including the request and response, are available at
TRACE
level.
- See also
com.twitter.finagle.ThriftMux.Client.methodBuilder to construct instances.
The user guide.
- Alphabetic
- By Inheritance
- MethodBuilder
- BaseMethodBuilder
- AnyRef
- Any
- Hide All
- Show All
- Public
- Protected
Instance Constructors
- new MethodBuilder(thriftMuxClient: ThriftMux.Client, mb: client.MethodBuilder[ThriftClientRequest, Array[Byte]])
Value Members
- final def !=(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
- final def ##: Int
- Definition Classes
- AnyRef → Any
- final def ==(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
- final def asInstanceOf[T0]: T0
- Definition Classes
- Any
- def clone(): AnyRef
- Attributes
- protected[lang]
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.CloneNotSupportedException]) @native()
- final def eq(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
- def equals(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef → Any
- def finalize(): Unit
- Attributes
- protected[lang]
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.Throwable])
- final def getClass(): Class[_ <: AnyRef]
- Definition Classes
- AnyRef → Any
- Annotations
- @native()
- def hashCode(): Int
- Definition Classes
- AnyRef → Any
- Annotations
- @native()
- def idempotent(maxExtraLoad: Tunable[Double], minSendBackupAfterMs: Int): MethodBuilder
<invalid inheritdoc annotation>
<invalid inheritdoc annotation>
This additionally causes Thrift Exceptions to be retried.
- def idempotent(maxExtraLoad: Tunable[Double]): MethodBuilder
Configure that requests are to be treated as idempotent.
Configure that requests are to be treated as idempotent. Because requests can be safely retried, BackupRequestFilter is configured with the params
maxExtraLoad
to decrease tail latency by sending an additional fraction of requests.This additionally causes Thrift Exceptions to be retried.
- maxExtraLoad
How much extra load, as a Tunable[Double], we are willing to send to the server. Must be between 0.0 and 1.0. Backup requests can be disabled by setting this to 0.0.
- Definition Classes
- MethodBuilder → BaseMethodBuilder
- def idempotent(maxExtraLoad: Double, minSendBackupAfterMs: Int): MethodBuilder
<invalid inheritdoc annotation>
<invalid inheritdoc annotation>
This additionally causes Thrift Exceptions to be retried.
- def idempotent(maxExtraLoad: Double): MethodBuilder
Configure that requests are to be treated as idempotent.
Configure that requests are to be treated as idempotent. Because requests can be safely retried, BackupRequestFilter is configured with the params
maxExtraLoad
to decrease tail latency by sending an additional fraction of requests.If you are using TwitterServer, a good starting point for determining a value for
maxExtraLoad
is looking at the details of the PDF histogram for request latency, at /admin/histograms. If you choose amaxExtraLoad
of 1.percent, for example, you can expect your p999/p9999 latencies to (roughly) now be that of your p99 latency. For 5.percent, those latencies would shift to your p95 latency. You should also ensure that your backend can tolerate the increased load.This additionally causes Thrift Exceptions to be retried.
- maxExtraLoad
How much extra load, as a fraction, we are willing to send to the server. Must be between 0.0 and 1.0. Backup requests can be disabled by setting this to 0.0.
- Definition Classes
- MethodBuilder → BaseMethodBuilder
- final def isInstanceOf[T0]: Boolean
- Definition Classes
- Any
- def label: String
Configured client label.
Configured client label. The
label
is used to assign a label to the underlying Thrift client. The label is used to display stats, etc. - final def ne(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
- def newService: Service[ThriftClientRequest, Array[Byte]]
Create a Service from the current configuration.
Create a Service from the current configuration.
- Note
It's very likely that you wanted/needed to use {{servicePerEndpoint}} instead.
- def newService(methodName: String): Service[ThriftClientRequest, Array[Byte]]
Create a Service from the current configuration.
Create a Service from the current configuration.
- Note
It's very likely that you wanted/needed to use {{servicePerEndpoint}} instead.
- def nonIdempotent: MethodBuilder
Configure that requests are to be treated as non-idempotent.
Configure that requests are to be treated as non-idempotent. BackupRequestFilter is disabled, and only those failures that are known to be safe to retry (i.e., write failures, where the request was never sent) are retried via requeue filter; any previously configured retries are removed.
- Definition Classes
- MethodBuilder → BaseMethodBuilder
- See also
The MethodBuilder section in the user guide for further details.
- final def notify(): Unit
- Definition Classes
- AnyRef
- Annotations
- @native()
- final def notifyAll(): Unit
- Definition Classes
- AnyRef
- Annotations
- @native()
- def servicePerEndpoint[ServicePerEndpoint <: Filterable[ServicePerEndpoint]](implicit builder: ServicePerEndpointBuilder[ServicePerEndpoint]): ServicePerEndpoint
Construct a
ServicePerEndpoint
to be used for the client. - def servicePerEndpoint[ServicePerEndpoint <: Filterable[ServicePerEndpoint]](methodName: String)(implicit builder: ServicePerEndpointBuilder[ServicePerEndpoint]): ServicePerEndpoint
Construct a
ServicePerEndpoint
to be used for themethodName
function.Construct a
ServicePerEndpoint
to be used for themethodName
function.- methodName
used for scoping metrics (e.g. "clnt/your_client_label/method_name").
- final def synchronized[T0](arg0: => T0): T0
- Definition Classes
- AnyRef
- def toString(): String
- Definition Classes
- MethodBuilder → AnyRef → Any
- final def wait(): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException])
- final def wait(arg0: Long, arg1: Int): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException])
- final def wait(arg0: Long): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException]) @native()
- def withMaxRetries(value: Int): MethodBuilder
Maximum retry attempts for a request based on the configured ResponseClassifier.
Maximum retry attempts for a request based on the configured ResponseClassifier.
- value
when a Failed with
retryable
istrue
is returned for a givenReqRep
, the request will be retried up tovalue
times or the com.twitter.finagle.service.RetryBudget has been exhausted (whichever occurs first).
- Definition Classes
- MethodBuilder → BaseMethodBuilder
- See also
The MethodBuilder section in the user guide for further details.
- def withPartitioningStrategy(strategy: PartitioningStrategy): MethodBuilder
Set a PartitioningStrategy for a MethodBuilder endpoint to enable partitioning awareness.
Set a PartitioningStrategy for a MethodBuilder endpoint to enable partitioning awareness. See PartitioningStrategy.
Default is com.twitter.finagle.thrift.exp.partitioning.Disabled
To set a hashing strategy to MethodBuilder:
import com.twitter.finagle.ThriftMux.Client import com.twitter.finagle.thrift.exp.partitioning.MethodBuilderHashingStrategy val hashingStrategy = new MethodBuilderHashingStrategy[RequestType, ResponseType](...) val client: ThriftMux.Client = ??? val builder = client.methodBuilder($address) builder .withPartitioningStrategy(hashingStrategy) .servicePerEndpoint... ...
Example: - def withRetryDisabled: MethodBuilder
Disables "application" level retries.
Disables "application" level retries.
This does not disable retries of failures that are known to be safe to retry via
com.twitter.finagle.service.RequeueFilter
.This causes the logical success metrics to be based on the default response classifier rules of a
Return
response is a success, while everything else is not.- Definition Classes
- MethodBuilder → BaseMethodBuilder
import com.twitter.finagle.http.Http val client: Http.Client = ??? val builder = client.methodBuilder("inet!example.com:80") builder.withRetryDisabled
- See also
The MethodBuilder section in the user guide for further details.
Example: - def withRetryForClassifier(classifier: ResponseClassifier): MethodBuilder
Retry based on ResponseClassifier.
Retry based on ResponseClassifier.
The default behavior is to use the client's classifier which is typically configured through
theClient.withResponseClassifier
orClientBuilder.withResponseClassifier
.This classifier is used to determine which requests are unsuccessful. This is the basis for measuring the logical success metrics as well as logging unsuccessful requests at debug level.
- classifier
when a Failed with
retryable
istrue
is returned for a givenReqRep
, the request will be retried. This is often aResponseClass.RetryableFailure
.
- Definition Classes
- MethodBuilder → BaseMethodBuilder
For example, retrying on a 418 status code:
import com.twitter.conversions.DurationOps._ import com.twitter.finagle.http.Http import com.twitter.finagle.service.{ReqRep, ResponseClass} import com.twitter.util.Return val client: Http.Client = ??? val builder = client.methodBuilder("inet!example.com:80") builder.withRetryForClassifier { case ReqRep(_, Return(rep)) if rep.statusCode == 418 => ResponseClass.RetryableFailure }
The classifier is also used to determine the logical success metrics of the client.
- See also
The MethodBuilder section in the user guide for further details.
Example: - def withTimeoutPerRequest(howLong: Tunable[Duration]): MethodBuilder
How long a single request is given to complete.
How long a single request is given to complete.
If there are retries, each attempt is given up to this amount of time.
If a request does not complete within this time, the response will be satisfied with a com.twitter.finagle.IndividualRequestTimeoutException.
Defaults to using the client's configuration for com.twitter.finagle.service.TimeoutFilter.Param(timeout), which is typically set via com.twitter.finagle.param.CommonParams.withRequestTimeout.
- howLong
how long, from the initial request issuance, an individual attempt given to complete. If it is not finite (e.g.
Duration.Top
), no method specific timeout will be applied.- returns
a new instance with all other settings copied
- Definition Classes
- MethodBuilder → BaseMethodBuilder
For example, a per-request timeout of 50 milliseconds:
import com.twitter.conversions.DurationOps._ import com.twitter.finagle.http.Http import com.twitter.util.Duration import com.twitter.util.tunable.Tunable val client: Http.Client = ??? val tunableTimeout: Tunable[Duration] = Tunable.const("id", 50.milliseconds) val builder = client.methodBuilder("inet!example.com:80") builder.withTimeoutPerRequest(tunableTimeout))
- See also
withTimeoutTotal(Tunable[Duration])
The MethodBuilder section in the user guide for further details.
Example: - def withTimeoutPerRequest(howLong: Duration): MethodBuilder
How long a single request is given to complete.
How long a single request is given to complete.
If there are retries, each attempt is given up to this amount of time.
If a request does not complete within this time, the response will be satisfied with a com.twitter.finagle.IndividualRequestTimeoutException.
Defaults to using the client's configuration for com.twitter.finagle.service.TimeoutFilter.Param(timeout), which is typically set via com.twitter.finagle.param.CommonParams.withRequestTimeout.
- howLong
how long, from the initial request issuance, an individual attempt given to complete. If it is not finite (e.g.
Duration.Top
), no method specific timeout will be applied.- returns
a new instance with all other settings copied
- Definition Classes
- MethodBuilder → BaseMethodBuilder
For example, a per-request timeout of 50 milliseconds:
import com.twitter.conversions.DurationOps._ import com.twitter.finagle.http.Http import com.twitter.util.Duration val client: Http.Client = ??? val builder = client.methodBuilder("inet!example.com:80") builder.withTimeoutPerRequest(50.milliseconds))
- See also
withTimeoutTotal(Duration)
The MethodBuilder section in the user guide for further details.
Example: - def withTimeoutTotal(howLong: Tunable[Duration]): MethodBuilder
Set a total timeout with a Tunable, including time spent on retries.
Set a total timeout with a Tunable, including time spent on retries.
If the request does not complete in this time, the response will be satisfied with a com.twitter.finagle.GlobalRequestTimeoutException.
Defaults to using the client's configuration for com.twitter.finagle.service.TimeoutFilter.TotalTimeout(timeout).
- howLong
how long, from the initial request issuance, is the request given to complete. If it is not finite (e.g.
Duration.Top
), no method specific timeout will be applied.- returns
a new instance with all other settings copied
- Definition Classes
- MethodBuilder → BaseMethodBuilder
For example, a total timeout of 200 milliseconds:
import com.twitter.conversions.DurationOps._ import com.twitter.finagle.http.Http import com.twitter.util.Duration import com.twitter.util.tunable.Tunable val client: Http.Client = ??? val tunableTimeout: Tunable[Duration] = Tunable.const("id", 200.milliseconds) val builder = client.methodBuilder("inet!example.com:80") builder.withTimeoutTotal(tunableTimeout))
- See also
withTimeoutPerRequest(Tunable[Duration])
The MethodBuilder section in the user guide for further details.
Example: - def withTimeoutTotal(howLong: Duration): MethodBuilder
Set a total timeout, including time spent on retries.
Set a total timeout, including time spent on retries.
If the request does not complete in this time, the response will be satisfied with a com.twitter.finagle.GlobalRequestTimeoutException.
Defaults to using the client's configuration for com.twitter.finagle.service.TimeoutFilter.TotalTimeout(timeout).
- howLong
how long, from the initial request issuance, is the request given to complete. If it is not finite (e.g.
Duration.Top
), no method specific timeout will be applied.- returns
a new instance with all other settings copied
- Definition Classes
- MethodBuilder → BaseMethodBuilder
For example, a total timeout of 200 milliseconds:
import com.twitter.conversions.DurationOps._ import com.twitter.finagle.http.Http import com.twitter.util.Duration val client: Http.Client = ??? val builder = client.methodBuilder("inet!example.com:80") builder.withTimeoutTotal(200.milliseconds))
- See also
withTimeoutPerRequest(Duration)
The MethodBuilder section in the user guide for further details.
Example:
Deprecated Value Members
- def newServiceIface[ServiceIface <: Filterable[ServiceIface]](methodName: String)(implicit builder: ServiceIfaceBuilder[ServiceIface]): ServiceIface
Construct a
ServiceIface
to be used for themethodName
function.Construct a
ServiceIface
to be used for themethodName
function.- methodName
used for scoping metrics (e.g. "clnt/your_client_label/method_name").
- Annotations
- @deprecated
- Deprecated
(Since version 2017-11-29) Use servicePerEndpoint