Skip to content

Commit 50265ef

Browse files
jcrossleyjenkins
authored and
jenkins
committed
Revert "Revert "finagle/finagle-core: Fix bug that causes backup requests to be disabled when configured via stack param in MethodBuilder""
This reverts commit 6b36c8bc38dd53e554f255a78b958c42cd032f3e. Differential Revision: https://phabricator.twitter.biz/D1194280
1 parent 596a803 commit 50265ef

File tree

3 files changed

+126
-16
lines changed

3 files changed

+126
-16
lines changed

CHANGELOG.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ Bug Fixes
4040
* finagle-memcached: Fixed support for running memcached tests with external memcached. Added README with
4141
instructions under finagle/finagle-memcached. ``PHAB_ID=D1120240``
4242
* finagle-core: Fixed a bug in `ExponentialJitteredBackoff` where `rng` can be 0. ``PHAB_ID=D1178528``
43+
* finagle-core: When `c.t.f.client.BackupRequestFilter` is configured via stack params,
44+
use this configuration when using MethodBuilder if `idempotent`/`nonIdempotent` have
45+
not been set. ``PHAB_ID=D1189436``
4346

4447

4548
Breaking API Changes

finagle-core/src/main/scala/com/twitter/finagle/client/MethodBuilder.scala

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ object MethodBuilder {
123123
retry: MethodBuilderRetry.Config,
124124
timeout: MethodBuilderTimeout.Config,
125125
filter: Filter.TypeAgnostic = Filter.typeAgnosticIdentity,
126-
backup: BackupRequestFilter.Param = BackupRequestFilter.Param.Disabled)
126+
backup: Option[BackupRequestFilter.Param] = None)
127127

128128
/** Used by the `ClientRegistry` */
129129
private[client] val RegistryKey = "methods"
@@ -396,7 +396,7 @@ final class MethodBuilder[Req, Rep] private[finagle] (
396396
dest,
397397
stack,
398398
params,
399-
config.copy(backup = brfParam)
399+
config.copy(backup = Some(brfParam))
400400
)
401401

402402
base.withRetry.forClassifier(idempotentedConfigClassifier)
@@ -436,7 +436,7 @@ final class MethodBuilder[Req, Rep] private[finagle] (
436436
dest,
437437
stack,
438438
params,
439-
config.copy(backup = BackupRequestFilter.Param.Disabled)
439+
config.copy(backup = Some(BackupRequestFilter.Param.Disabled))
440440
).withRetry.forClassifier(nonidempotentedConfigClassifier)
441441
}
442442

@@ -677,9 +677,16 @@ final class MethodBuilder[Req, Rep] private[finagle] (
677677

678678
val underlying = methodPool.get
679679

680-
val backupRequestParams = params +
681-
config.backup +
682-
param.ResponseClassifier(config.retry.responseClassifier)
680+
// If the user has not configured the backup requests via MethodBuilder (via `idempotent`,
681+
// of `nonIdempotent`), use the configuration from the params (Disabled if none available).
682+
val backupRequestParams = config.backup match {
683+
case Some(backupRequestParam) =>
684+
params +
685+
backupRequestParam +
686+
param.ResponseClassifier(config.retry.responseClassifier)
687+
case None =>
688+
params + param.ResponseClassifier(config.retry.responseClassifier)
689+
}
683690

684691
// register BackupRequestFilter under the same prefixes as other method entries
685692
val prefixes = Seq(registryEntry().addr) ++ registryKeyPrefix(name)

finagle-core/src/test/scala/com/twitter/finagle/client/MethodBuilderTest.scala

Lines changed: 110 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -658,16 +658,19 @@ class MethodBuilderTest
658658

659659
val stackClient = TestStackClient(stack, params)
660660
val initialMethodBuilder = MethodBuilder.from("with backups", stackClient)
661+
assert(initialMethodBuilder.config.backup == None)
662+
661663
val methodBuilder =
662-
initialMethodBuilder.withConfig(initialMethodBuilder.config.copy(backup = configuredBrfParam))
664+
initialMethodBuilder.withConfig(
665+
initialMethodBuilder.config.copy(backup = Some(configuredBrfParam)))
663666

664667
// Ensure BRF is configured before calling `nonIdempotent`
665-
assert(methodBuilder.config.backup == configuredBrfParam)
668+
assert(methodBuilder.config.backup == Some(configuredBrfParam))
666669

667670
val nonIdempotentClient = methodBuilder.nonIdempotent
668671

669672
// Ensure BRF is disabled after calling `nonIdempotent`
670-
assert(nonIdempotentClient.config.backup == BackupRequestFilter.Disabled)
673+
assert(nonIdempotentClient.config.backup == Some(BackupRequestFilter.Disabled))
671674
}
672675

673676
test("nonIdempotent client keeps existing ResponseClassifier in params ") {
@@ -796,8 +799,9 @@ class MethodBuilderTest
796799
.idempotent(1.percent, sendInterrupts = true, classifier)
797800

798801
mb.config.backup match {
799-
case BackupRequestFilter.Param
800-
.Configured(maxExtraLoadTunable, sendInterrupts, minSendBackupAfterMs) =>
802+
case Some(
803+
BackupRequestFilter.Param
804+
.Configured(maxExtraLoadTunable, sendInterrupts, minSendBackupAfterMs)) =>
801805
assert(
802806
maxExtraLoadTunable().get == 1.percent && sendInterrupts && minSendBackupAfterMs == 1)
803807
case _ => fail("BackupRequestFilter not configured")
@@ -862,8 +866,9 @@ class MethodBuilderTest
862866
.idempotent(tunable, sendInterrupts = true, ResponseClassifier.Default)
863867

864868
assert(
865-
mb.config.backup == BackupRequestFilter
866-
.Configured(tunable, sendInterrupts = true)
869+
mb.config.backup == Some(
870+
BackupRequestFilter
871+
.Configured(tunable, sendInterrupts = true))
867872
)
868873
}
869874

@@ -880,12 +885,13 @@ class MethodBuilderTest
880885
.idempotent(tunable, sendInterrupts = true, ResponseClassifier.Default)
881886

882887
assert(
883-
mb.config.backup == BackupRequestFilter
884-
.Configured(tunable, sendInterrupts = true)
888+
mb.config.backup == Some(
889+
BackupRequestFilter
890+
.Configured(tunable, sendInterrupts = true))
885891
)
886892

887893
val nonIdempotentMB = mb.nonIdempotent
888-
assert(nonIdempotentMB.config.backup == BackupRequestFilter.Disabled)
894+
assert(nonIdempotentMB.config.backup == Some(BackupRequestFilter.Disabled))
889895
}
890896

891897
test("idempotent combines existing classifier with new one") {
@@ -1122,6 +1128,100 @@ class MethodBuilderTest
11221128
assert(client.params[Retries.Budget].retryBudget eq retryBudget)
11231129
}
11241130

1131+
test(
1132+
"BackupRequestFilter is configured with passed-in stack params when not configured in MethodBuilder") {
1133+
val stats = new InMemoryStatsReceiver()
1134+
val timer = new MockTimer()
1135+
val params =
1136+
Stack.Params.empty +
1137+
param.Stats(stats) +
1138+
param.Timer(timer) +
1139+
BackupRequestFilter.Configured(maxExtraLoad = 0.01, sendInterrupts = true)
1140+
1141+
val svc: Service[Int, Int] = Service.mk { i =>
1142+
Future.value(i)
1143+
}
1144+
1145+
val stack = Stack.leaf(Stack.Role("test"), ServiceFactory.const(svc))
1146+
1147+
val stackClient = TestStackClient(stack, Stack.Params.empty).withParams(params)
1148+
val mb = MethodBuilder.from("mb", stackClient)
1149+
1150+
Time.withCurrentTimeFrozen { tc =>
1151+
val client = mb.newService("a_client")
1152+
awaitResult(client(1))
1153+
1154+
tc.advance(10.seconds)
1155+
timer.tick()
1156+
assert(stats.stats.contains(Seq("mb", "a_client", "backups", "send_backup_after_ms")))
1157+
}
1158+
}
1159+
1160+
test(
1161+
"BackupRequestFilter is configured with MethodBuilder idempotent configuration when also configured via stack params") {
1162+
val stats = new InMemoryStatsReceiver()
1163+
val timer = new MockTimer()
1164+
val params =
1165+
Stack.Params.empty +
1166+
param.Stats(stats) +
1167+
param.Timer(timer) +
1168+
BackupRequestFilter.Disabled
1169+
1170+
val svc: Service[Int, Int] = Service.mk { i =>
1171+
Future.value(i)
1172+
}
1173+
1174+
val stack = Stack.leaf(Stack.Role("test"), ServiceFactory.const(svc))
1175+
1176+
val stackClient = TestStackClient(stack, Stack.Params.empty).withParams(params)
1177+
val classifier: ResponseClassifier = ResponseClassifier.named("foo") {
1178+
case ReqRep(_, Throw(_: IndividualRequestTimeoutException)) =>
1179+
ResponseClass.RetryableFailure
1180+
}
1181+
1182+
// MB config should take precedence
1183+
val mb = MethodBuilder.from("mb", stackClient).idempotent(0.05, true, classifier)
1184+
1185+
Time.withCurrentTimeFrozen { tc =>
1186+
val client = mb.newService("a_client")
1187+
awaitResult(client(1))
1188+
1189+
tc.advance(10.seconds)
1190+
timer.tick()
1191+
assert(stats.stats.contains(Seq("mb", "a_client", "backups", "send_backup_after_ms")))
1192+
}
1193+
}
1194+
1195+
test(
1196+
"BackupRequestFilter is configured with MethodBuilder nonIdempotent configuration when also configured via stack params") {
1197+
val stats = new InMemoryStatsReceiver()
1198+
val timer = new MockTimer()
1199+
val params =
1200+
Stack.Params.empty +
1201+
param.Stats(stats) +
1202+
param.Timer(timer) +
1203+
BackupRequestFilter.Configured(maxExtraLoad = 0.01, sendInterrupts = true)
1204+
1205+
val svc: Service[Int, Int] = Service.mk { i =>
1206+
Future.value(i)
1207+
}
1208+
1209+
val stack = Stack.leaf(Stack.Role("test"), ServiceFactory.const(svc))
1210+
1211+
val stackClient = TestStackClient(stack, Stack.Params.empty).withParams(params)
1212+
// MB config should take precedence
1213+
val mb = MethodBuilder.from("mb", stackClient).nonIdempotent
1214+
1215+
Time.withCurrentTimeFrozen { tc =>
1216+
val client = mb.newService("a_client")
1217+
awaitResult(client(1))
1218+
1219+
tc.advance(10.seconds)
1220+
timer.tick()
1221+
assert(!stats.stats.contains(Seq("mb", "a_client", "backups", "send_backup_after_ms")))
1222+
}
1223+
}
1224+
11251225
test("shares RetryBudget between methods") {
11261226
val params = StackClient.defaultParams
11271227

0 commit comments

Comments
 (0)