Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

refactor: Added protocol callback error handling #393

Closed
wants to merge 1 commit into from
Closed

refactor: Added protocol callback error handling #393

wants to merge 1 commit into from

Conversation

soluchok
Copy link
Contributor

@soluchok soluchok commented Oct 2, 2019

  • ability to consume the result of the callback function

Closes: #242

Signed-off-by: Andrii Soluk isoluchok@gmail.com

@codecov
Copy link

codecov bot commented Oct 2, 2019

Codecov Report

Merging #393 into master will increase coverage by 0.05%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #393      +/-   ##
==========================================
+ Coverage   89.63%   89.69%   +0.05%     
==========================================
  Files          56       56              
  Lines        3271     3289      +18     
==========================================
+ Hits         2932     2950      +18     
  Misses        183      183              
  Partials      156      156
Impacted Files Coverage Δ
pkg/didcomm/protocol/didexchange/service.go 94.84% <100%> (+0.43%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 238da0b...8c5ff82. Read the comment docs.

@troyronda
Copy link
Contributor

@soluchok normal semantic PR prefixes: https://github.com/commitizen/conventional-commit-types/blob/v2.2.0/index.json (from https://github.com/probot/semantic-pull-requests)

ie refactor not refactoring

@soluchok soluchok changed the title refactoring: Added error handling refactor: Added error handling Oct 2, 2019
@rolsonquadras
Copy link
Contributor

@soluchok issue #242 is a feature issue. The PR title says "refactor". Should the title be updated with "feat" ?

@soluchok soluchok changed the title refactor: Added error handling feat: Added error handling Oct 2, 2019
msg := &dispatcher.DIDCommCallback{}
err = msg.Done(dispatcher.Result{Err: err})
require.NoError(t, err)
e.Callback(msg)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you please add a test case to validate the error from callback processing ? i.e, consumer calls the callback and validates the error from the callback.

Copy link
Contributor Author

@soluchok soluchok Oct 2, 2019

Choose a reason for hiding this comment

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

@troyronda
Copy link
Contributor

@rolsonquadras @soluchok "feat: Added error handling" will be a meaningless entry in the change log.

@soluchok soluchok changed the title feat: Added error handling refactor: Added callback error handling Oct 2, 2019
@troyronda troyronda changed the title refactor: Added callback error handling refactor: Added dispatcher callback error handling Oct 2, 2019
@troyronda troyronda changed the title refactor: Added dispatcher callback error handling refactor: Added protocol callback error handling Oct 2, 2019
@rolsonquadras
Copy link
Contributor

@soluchok issue #242 is a feature issue. The PR title says "refactor". Should the title be updated with "feat" ?

Any specific reason to revert back to "refactor". Also the commit message doesn't have semantic prefix.

@soluchok
Copy link
Contributor Author

soluchok commented Oct 2, 2019

@soluchok issue #242 is a feature issue. The PR title says "refactor". Should the title be updated with "feat" ?

Any specific reason to revert back to "refactor". Also the commit message doesn't have semantic prefix.

Ok, let's decide is it refactor or feat? @troyronda @rolsonquadras
and I will change comment and title

@rolsonquadras
Copy link
Contributor

@soluchok issue #242 is a feature issue. The PR title says "refactor". Should the title be updated with "feat" ?

Any specific reason to revert back to "refactor". Also the commit message doesn't have semantic prefix.

Ok, let's decide is it refactor or feat? @troyronda @rolsonquadras
and I will change comment and title

My bad, missed @troyronda 's comment.

pkg/didcomm/dispatcher/api.go Outdated Show resolved Hide resolved
pkg/didcomm/dispatcher/api.go Outdated Show resolved Hide resolved
pkg/didcomm/dispatcher/api.go Outdated Show resolved Hide resolved
pkg/didcomm/protocol/didexchange/service_test.go Outdated Show resolved Hide resolved
pkg/didcomm/dispatcher/api.go Outdated Show resolved Hide resolved
pkg/didcomm/protocol/didexchange/service_test.go Outdated Show resolved Hide resolved
pkg/didcomm/protocol/didexchange/service.go Outdated Show resolved Hide resolved
@troyronda
Copy link
Contributor

troyronda commented Oct 7, 2019

This PR may need a discussion. I chatted with @rolsonquadras about the approach and he plans to write something up.

Briefly my thoughts were:

  • Callback is currently handling two scenarios (Client wants to signal 'Continue with Protocol' vs 'Fail/Stop Protocol'). Suggest these be separated (e.g., Fail doesn't need an error channel).
  • For a client to have handle (multiple) async scenarios, it would have been logical for that client to pass-in a channel for receiving error events. In this way they could setup a for { select {} } for all event handling.
  • I still don't fully understand the done scenario (and the code for it seemed complex).
  • For a client that handles sync scenarios, they would probably wrap the callback error channel into a blocking call ( ending up with err := continueWrapper(...) )

@rolsonquadras
Copy link
Contributor

This PR may need a discussion. I chatted with @rolsonquadras about the approach and he plans to write something up.

The current consumer code looks something like follows without error handling (master branch).

	actionCh := make(chan dispatcher.DIDCommAction)
	err := svc.RegisterActionEvent(actionCh)
	for {
		select {
			case e := <-c.actionCh:
				e.Continue() 	// e.Callback() is changed to e.Continue  #437
				// TODO handle error from callback
		}
	}

With the error handling code added in this PR, the consumer code will look like below.

	actionCh := make(chan dispatcher.DIDCommAction)
	err := svc.RegisterActionEvent(actionCh)
	for {
		select {
			case e := <-c.actionCh:
				msg := dispatcher.NewDIDCommCallback()
				for {
					select {
					case res := <-msg.Result():
						// process error
				}()
				e.Continue(msg)
		}
	}

This would block the main select(actionCh) and other actionEvents will buffer. This would be similar to sync call err := e.Continue().

Briefly my thoughts were:

  • Callback is currently handling two scenarios (Client wants to signal 'Continue with Protocol' vs 'Fail/Stop Protocol'). Suggest these be separated (e.g., Fail doesn't need an error channel).

This has been handled through #436 and #437.

  • For a client to have handle (multiple) async scenarios, it would have been logical for that client to pass-in a channel for receiving error events. In this way they could setup a for { select {} } for all event handling.
	actionCh := make(chan dispatcher.DIDCommAction)
	err := svc.RegisterActionEvent(actionCh)
	errorCh := make(chan <some_type)
	
	for {
		select {
			case e := <-c.actionCh:
				e.Continue(errorCh)
			case e := <-errorCh:
				// process the error
				// correlate based on ConnectionID - https://github.com/hyperledger/aries-framework-go/blob/master/pkg/didcomm/protocol/didexchange/service.go#L254
		}
	}

@troyronda @soluchok

@soluchok
Copy link
Contributor Author

soluchok commented Oct 7, 2019

@rolsonquadras

With the error handling code added in this PR, the consumer code will look like below.

	actionCh := make(chan dispatcher.DIDCommAction)
	err := svc.RegisterActionEvent(actionCh)
	for {
		select {
		case e := <-c.actionCh:
			msg := dispatcher.NewDIDCommCallback()
                        e.Continue(msg)
                        go func(m *dispatcher.DIDCommCallback){
			    if  res := <-m.Result(); res.Err != nil {
				e.Stop(res.Err)
			    }
                        }(msg)
		}
	}

@rolsonquadras
Copy link
Contributor

@rolsonquadras

With the error handling code added in this PR, the consumer code will look like below.

	actionCh := make(chan dispatcher.DIDCommAction)
	err := svc.RegisterActionEvent(actionCh)
	for {
		select {
		case e := <-c.actionCh:
			msg := dispatcher.NewDIDCommCallback()
                        e.Continue(msg)
                        go func(m *dispatcher.DIDCommCallback){
			    if  res := <-m.Result(); res.Err != nil {
				e.Stop(res.Err)
			    }
                        }(msg)
		}
	}

Consumer will either invoke either Continue() or Stop(). For Stop(), consumer won't send back the error received from the callback. It's the error from action processing.

@troyronda troyronda self-requested a review October 8, 2019 11:12
// DIDCommAction message type to pass events in go channels.
type DIDCommAction struct {
// DIDComm message
Message DIDCommMsg
// Continue function to be called by the consumer for further processing the message.
Continue func()
Continue func(chan<- ActionError)
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be nice if we could avoid having to pas a nil argument when the caller doesn’t care about the error chan. I wonder if we should use a variadic pattern?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In my opinion, better to pass a clear argument, it's much easier to work with it. But options are not a bad approach, especially for callback (probably it will require one more option in the future)

Copy link
Contributor

Choose a reason for hiding this comment

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

I like Rolson's snippet towards the end of this comment better. It would be simpler from a user perspective if the error message has a Retry() function or something similar.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It has res.Action.Continue() if you need retry it

e.Continue(eChan)
select {
case res := <-eChan:
require.EqualError(t, res.Err, "document for the id doesn't exists in the database: data not found")
Copy link
Contributor

Choose a reason for hiding this comment

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

it would be good to have a test case showing correlation.

Signed-off-by: Andrii Soluk <isoluchok@gmail.com>
@soluchok soluchok closed this Oct 9, 2019
@soluchok soluchok deleted the issue-242 branch October 22, 2019 16:17
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Development

Successfully merging this pull request may close these issues.

Service - Retry logic
6 participants