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

Replace unsafe usage of recover() in helper functions #4913

Merged
merged 5 commits into from
Jul 26, 2022
Merged

Conversation

ZackLK
Copy link
Contributor

@ZackLK ZackLK commented Jul 23, 2022

We decided that our usage of helper functions when using defer/recover was too brittle, since one
too many functions causes recover() not to do the right thing in Go. Switching to this idiom, where
the defer caller also calls recover() ensures that we will get the right result from recover(), but
also lets us continue to use the helper functions to deduplicate the code that runs after recover().

@ZackLK ZackLK requested review from Groxx and a team July 23, 2022 04:53
@ZackLK ZackLK enabled auto-merge (squash) July 23, 2022 04:53
@coveralls
Copy link

Pull Request Test Coverage Report for Build 018231b4-2087-4f61-ad95-2483244b3ff8

  • 125 of 212 (58.96%) changed or added relevant lines in 9 files are covered.
  • 139 unchanged lines in 12 files lost coverage.
  • Overall coverage decreased (-0.06%) to 57.708%

Changes Missing Coverage Covered Lines Changed/Added Lines %
common/persistence/sql/sqlExecutionStore.go 20 22 90.91%
client/history/client.go 0 3 0.0%
service/matching/handler.go 7 10 70.0%
service/frontend/clusterRedirectionHandler.go 35 40 87.5%
service/frontend/workflowHandler.go 31 41 75.61%
service/history/handler.go 24 40 60.0%
service/frontend/adminHandler.go 6 26 23.08%
common/persistence/sql/sqlExecutionStoreUtil.go 0 28 0.0%
Files with Coverage Reduction New Missed Lines %
service/history/queue/timer_queue_processor_base.go 1 77.26%
common/persistence/sql/sqlExecutionStore.go 2 62.4%
common/persistence/sql/sqlExecutionStoreUtil.go 2 50.9%
service/matching/taskListManager.go 2 73.77%
service/history/task/fetcher.go 4 91.28%
common/persistence/statsComputer.go 6 93.57%
common/persistence/serialization/parser.go 8 62.41%
common/persistence/serialization/thrift_decoder.go 8 53.06%
common/persistence/nosql/nosqlplugin/cassandra/workflowUtils.go 21 74.76%
service/history/execution/mutable_state_task_refresher.go 22 66.46%
Totals Coverage Status
Change from base Build 01822959-de4a-45f0-b67a-2dee11aa2ea7: -0.06%
Covered Lines: 83766
Relevant Lines: 145156

💛 - Coveralls

@Groxx
Copy link
Contributor

Groxx commented Jul 26, 2022

Now that it's merged, want to see if mgechev/revive#719 can be pulled in easily enough? go get -modfile internal/tools/go.mod ... iirc. That plus adding "immediate-recover" to the defer list in the toml will (hopefully) require that pattern.

Copy link
Contributor

@Groxx Groxx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 all LGTM. And I am glad that gofmt is permissive about this being one line...

Comment on lines +34 to 41
// errPanic MUST be the result from calling recover, which MUST be done in a single level deep
// deferred function. The usual way of calling this is:
// - defer func() { log.CapturePanic(recover(), logger, &err) }()
func CapturePanic(errPanic interface{}, logger Logger, retError *error) {
if errPanic != nil {
err, ok := errPanic.(error)
if !ok {
err = fmt.Errorf("panic object is not error: %#v", errPanic)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll lightly vote for a rename, but either's clear enough I think. and there is some benefit in implying something may be panicking error objects and they're handled specially...

eh. your pick.

Suggested change
// errPanic MUST be the result from calling recover, which MUST be done in a single level deep
// deferred function. The usual way of calling this is:
// - defer func() { log.CapturePanic(recover(), logger, &err) }()
func CapturePanic(errPanic interface{}, logger Logger, retError *error) {
if errPanic != nil {
err, ok := errPanic.(error)
if !ok {
err = fmt.Errorf("panic object is not error: %#v", errPanic)
// "recovered" MUST be the result from calling recover, which MUST be done in a single level deep
// deferred function. The usual way of calling this is:
// - defer func() { log.CapturePanic(recover(), logger, &err) }()
func CapturePanic(recovered interface{}, logger Logger, retError *error) {
if recovered != nil {
err, ok := recovered.(error)
if !ok {
err = fmt.Errorf("panic object is not error: %#v", recovered)

@ZackLK ZackLK merged commit 82544de into master Jul 26, 2022
@ZackLK ZackLK deleted the defer_recover2 branch July 26, 2022 01:45
ZackLK added a commit that referenced this pull request Aug 22, 2022
We decided that our usage of helper functions when using defer/recover was too brittle, since one
too many functions causes recover() not to do the right thing in Go. Switching to this idiom, where
the defer caller also calls recover() ensures that we will get the right result from recover(), but
also lets us continue to use the helper functions to deduplicate the code that runs after recover().
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants