Skip to content
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

能在详细讲解一下replay、restore的设计理念吗?没有思考明白,为什么还需要有恢复步骤 #201

Closed
ruanzhi opened this issue Sep 7, 2020 · 4 comments
Assignees

Comments

@ruanzhi
Copy link

ruanzhi commented Sep 7, 2020

提供反例式的回答

如果直接用map,在capture 时清理map中的数据,同时在restore 清理map中的数据,会有什么影响?

你说的是 map 是指 holder吗?如果是,说明如下:

『在capture 时清理map中的数据』 的问题

ExecutorService executorService = ...

Runnable bizTask = ...
// <doCaptrue>  
// 在capture 时清理Holder中的数据
Runnable ttlRunnable = TtlRunnable.get();

executorService.submit(ttlRunnable);

// 后续运行,因为 Holder 没有KV了,即 后续的传递的内容是空。
// 都不会再正确传递,恢复成空。
// !!Bug!!

Runnable 后续的bizTask = ...
Runnable 后续的ttlRunnable = TtlRunnable.get();

executorService.submit(ttlRunnable);

『在restore 清理map中的数据』 的问题

提交到线程池的任务 可能 在本线程直接执行(参见 CallerRunsPolicy

问题说明如下:

Runnable bizTask = ...
Runnable ttlRunnable = TtlRunnable.get();

// <doRestore>  
// 在restore 清理map中的数据
// 且是 本线程直接执行时,
executorService.submit(ttlRunnable);

// 后续运行,因为 Holder 没有KV了,即 后续的传递的内容是空。
// 都不会再正确传递,恢复成空。
// !!Bug!!

在run 的 finally 中restore 的原因也没想明白,

  1. 如果线程结束了,那么 把之前的 TransmittableThreadLocal restore回来有什么意义?
  2. 线程复用的话,应该是在下次线程执行时复用 启动线程的 TransmittableThreadLocal,而不是restore 的TransmittableThreadLocal。

还是 因为 『提交到线程池的任务 可能 在本线程直接执行』『Restore』确保没有上面的 Bug

按原则的回答

原则:通过 整体流程/设计/代码实现 来 分析/证明 正确性。 @soca2013

CRR(Capture/Replay/Restore)是一个面向上下文传递设计的流程,通过这个流程的分析可以保证/证明 正确性。

这个正确性的分析/证明 ,不依赖于 局部与反例。

总结一下:尽量首先去确定分析自己程序的正确性,而不是找反例。不分析而去依赖反例,又因为经验受限找不到反例认为没问题而上线,这其实就是我们程序出bug的原因。


@soca2013 有说得不明白的地方,欢迎交流。 ❤️

PS

如果你有兴趣『整体流程与分析』推荐:

Originally posted by @oldratlee in #145 (comment)

@ruanzhi
Copy link
Author

ruanzhi commented Sep 7, 2020

我理解直接在在restore里面将holder里面所有的清理掉就OK了,为啥还需要恢复backup。
backup什么情况下才会有值呢?我理解如果是线程池中的线程,backup不可能有值存在。

@oldratlee
Copy link
Member

oldratlee commented Sep 9, 2020

backup什么情况下才会有值呢?

  • 典型的业务场景下,replay操作的线程,与来源的capture线程,是不同的。
  • capture的线程 在 业务中立的线程池 时,这样的线程 往往 也没有/不需要 有上下文。

这2个前提成立时,backup往往 不会有值。

当上面2点不成立时,如

  • 上面提到的场景,线程池满了 且 线程池使用的是CallerRunsPolicy
    则 提交到线程池的任务 在capture线程直接执行,也就是 直接在业务线程中同步执行;
  • 使用ForkJoinPool(包含并行执行StreamCompletableFuture,底层使用ForkJoinPool)的场景,展开的ForkJoinTask会在调用线程中直接执行。

这时 backup是有值的,如果不做restore backup业务线程里的上下文就丢了,
业务后续的执行就会有Bug@ruanzhi

上面这个线程池场景,因为线程池的广泛大量使用,
是日常业务开发会碰到的问题(在线上,也解决过这样的问题)。

另外,如果用了像Reactive Programming(RP/反应式编程)这样的技术,
业务逻辑 完全是在Reactive接管的 调度器(Scheduler)/线程池 里执行的,
相应的问题出现的可能性就更高了。

@oldratlee oldratlee added the ❓question Further information is requested label Sep 9, 2020
@oldratlee oldratlee self-assigned this Sep 9, 2020
@ruanzhi
Copy link
Author

ruanzhi commented Sep 10, 2020

『CallerRunsPolicy』情况理解,我再看看 反应式编程,非常感谢

@oldratlee
Copy link
Member

@ruanzhi 好,有说的不清楚的,欢迎继续讨论 ♥️

这个Issue先Close了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants