-
-
Notifications
You must be signed in to change notification settings - Fork 243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Propagate Local isolation in runToFuture #1213
Conversation
…oFuture-isolation # Conflicts: # monix-eval/jvm/src/test/scala/monix/eval/TaskLocalJVMSuite.scala # monix-eval/shared/src/main/scala/monix/eval/Task.scala # monix-eval/shared/src/main/scala/monix/eval/internal/TaskRunLoop.scala
0a26f35
to
206495e
Compare
Sorry for the late response, will check it out during the week! |
Okay so I ran my tests against this snapshots and this is the problem that I am getting. With mdedetrich/monix-mdc#2 the following test fails
And with mdedetrich/monix-opentracing#2 FutureTaskTracingSchedulerSpec
and FutureTaskScalaConcurrentSpec
So I think it may have fix some issues but created others? Also my own mdedetrich/akka-monix-test#1 seems to be failing (the main difference between my own version and your version is that I am using |
Thanks, I have a limited access to the Internet right now but I will look at your test cases as soon as I'm able |
# Conflicts: # monix-execution/shared/src/main/scala/monix/execution/CancelableFuture.scala
@@ -31,7 +32,7 @@ import scala.util.control.NonFatal | |||
/** Represents an asynchronous computation that can be canceled | |||
* as long as it isn't complete. | |||
*/ | |||
sealed abstract class CancelableFuture[+A] extends Future[A] with Cancelable { self => | |||
sealed abstract class CancelableFuture[+A](isolatedCtx: Local.Context) extends Future[A] with Cancelable { self => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Temporary solution to see if it's worth pursuing - if it works, I'll hide everything
I was pretty sure that my previous attempt was consistently passing tests but when I tried it now, it's not the case so it seems like I was deceived. I tried a different angle that is available in: I tested on your There is at least one limitation: More precisely: task.runToFuture.flatMap(_ => Future()).flatMap(_ => Future()) is OK task.runToFuture.fast.flatMap(_ => Future()).flatMap(_ => Future()) is NOT OK because it wraps As a consequence, def onCompleteLocal[T](task: => Task[T]): Directive1[Try[T]] = {
Directive { inner => ctx =>
task.runToFutureOpt.transformWith(t => inner(Tuple1(t))(ctx))
}
} I feel like it could be OK in practice because we could provide these directives in There also shouldn't be onCompleteLocal(Task {
Local.isolate {
MDC.put("local", id.toString)
}
})
|
BTW I feel like def getAndPutTask(key: String, value: String): Future[String] =
for {
_ <- Task {
MDC.put(key, value)
}.runToFutureOpt
get <- Future {
MDC.getCopyOfContextMap.size shouldBe 1
MDC.get(key)
}
} yield get
"Write with Task and get in Future inside Future for comprehension concurrently" in {
val keyValues = MultipleKeysMultipleValues.multipleKeyValueGenerator.sample.get
val futures = keyValues.keysAndValues.map { keyValue =>
getAndPutTask(keyValue.key, keyValue.value)
}
val future = Future.sequence(futures)
future.map { retrievedKeyValues =>
retrievedKeyValues.size shouldBe keyValues.keysAndValues.size
retrievedKeyValues shouldBe keyValues.keysAndValues.map(_.value)
}
} Note |
I have tried
Change was simple: implicit val scheduler: Scheduler = TracingScheduler(AsyncScheduler(
Scheduler.DefaultScheduledExecutor,
new TracedExecutionContext(ExecutionContext.Implicits.global, tracer),
ExecutionModel.Default,
UncaughtExceptionReporter.default
)) If In the following days, I'll do more testing and try to implement it in a binary compatible way. |
My only comment is on task.runToFuture.fast.flatMap(_ => Future()).flatMap(_ => Future())
I will have to look into this because akka-http uses fast future internally and because of this we may have issues achieving the ultimate goal (which is being able to pass locals around from routes down to business logic implemented in Monix). Apart from that everything is looking good, let me know if you need anything else! |
Thanks @mdedetrich - I don't have access to any Akka HTTP codebase right now so I will appreciate investigation there! if |
I will look at it mid next week, I have holidays then so plenty of time to look into it properly. |
Any update @mdedetrich ? The latest snapshot is |
@Avasil Unfortunately I don't really have the time to test this properly and at the same time I don't want to block this so I think unless there is a strong reason otherwise it makes sense to make a release with this functionality and improve on it later. I am however still curious as to why the |
…oFuture-isolation
The current implementation works because
If we keep isolated testAsync("Task.eval.runToFuture is isolated from outside changes") {
implicit val s: Scheduler = Scheduler.Implicits.traced
val local = Local(0)
val t1 = for {
i1 <- Task(local.get)
_ <- Task.sleep(10.millis)
i2 <- Task(local.get)
} yield assertEquals(i1, i2)
val f = t1.runToFuture
local.update(100)
f
} I'm experimenting with an alternative solution: instead of returning the original The first issue that comes to my mind is that it is not consistent with other "run" operators, like |
Well, as I think about it, it seems more and more like a bad idea. If we leave the copy on the current thread, then each subsequent request will start with previous request's metadata unless it's overwritten/cleared. I will just release the current state and think about it more later, or perhaps leave it as is depending on user's feedback. |
# Conflicts: # monix-eval/shared/src/main/scala/monix/eval/internal/TaskRunLoop.scala # project/MimaFilters.scala
So if I understand the PR correctly, we currently have support for isolation when we use |
Yes, I don't want to delay it any further and would love to include it in the release in a few days |
Fixes #848
@jvican @mdedetrich
This is what I have right now.
The change is to only restore parent
Local
on the current Thread and call the Future callback with isolated one which will be kept by eachFuture
continuation.I have released a SNAPSHOT:
3.3.0-69f970a-SNAPSHOT
If you could check if it works for your use case then it will be very helpful :)
I need to do more testing, check performance impact, consider more use cases (e.g. mapping completed future) etc.
I expect bugs but I feel like it might be already usable for the main use case.
I did a simple test: ( modify local in
Task
, read in Akka HTTP Directive)And then ran many concurrent requests with unique "id".
Each
Task
retained it so it looks promising!