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

fix-action-id #31

Closed
wants to merge 9 commits into from
Closed

fix-action-id #31

wants to merge 9 commits into from

Conversation

mind-ar
Copy link
Contributor

@mind-ar mind-ar commented Mar 28, 2021

added validation for actionID in command response
fixes #14
fixes #22

Manuel added 2 commits March 28, 2021 01:44
added validation for actionID in command response
@mind-ar
Copy link
Contributor Author

mind-ar commented Mar 28, 2021

hi, I was finally able to solve the command collision problem without adding an event broker: added a mutex in the "send" and "requestList" functions.
you can reproduce the command collision with the following example:

	go func() {
		for {
			time.Sleep(300 * time.Millisecond)
			if err := ami.Ping(ctx, amiSocket, ""); err != nil {
				log.Error().Err(err).Msg("Error en Ping")
			}
			log.Debug().Msg("Ping Ok")

		}
	}()
	go func() {
		for {
			time.Sleep(200 * time.Millisecond)
			if err := ami.Ping(ctx, amiSocket, ""); err != nil {
				log.Error().Err(err).Msg("Error en Ping")
			}
			log.Debug().Msg("Ping Ok")

		}
	}()

@heltonmarx
Copy link
Owner

The goami connection is single threaded.

If you reuse a single connection between multiple threads and commands it will block other threads until one thread complete with connection.

So, the easy solution is create a separate goami connection to each thread, that could overhead the asterisk server.

The hard solution it's create a connection pool.

I believe that what we have with your implementation partly fix that, but I'd like to suggest this Object Pool Design Pattern.

keeping the fix in requestList method:

responses := make([]Response, 0)
for {
  response, err := read(ctx, client)
  if err != nil {
    return nil, err
  }
  if response.Get("ActionID") != id {
    continue
  }
  e := response.Get("Event")
  r := response.Get("Response")
  if e == event {
    responses = append(responses, response)
   } else if e == complete || r != "" && r != "Success" {
     break
  }
}
return responses, nil

Suggestion:

Modify your PR to just to fix the actionID (like described in the title).
And in this mean create another patch with connection pool only.

@mind-ar
Copy link
Contributor Author

mind-ar commented Apr 1, 2021

Hi, thanks for your time!
i've followed your suggestion, and reverted the last change (mutex).
i will create a PR with connection pool for reviewing

Manuel and others added 2 commits April 1, 2021 19:58
added validation for actionID in command response


(cherry picked from commit 6621655)
fix-action-id

See merge request mind-framework/asterisk/goami!1
@mind-ar
Copy link
Contributor Author

mind-ar commented Jul 6, 2021

Hi there, let me know if any correction to this PR is needed.
thanks!

ami/utils.go Outdated Show resolved Hide resolved
ami/utils.go Outdated
b, err := command(action, id, v)
if err != nil {
return nil, err
}
if err := client.Send(string(b)); err != nil {
return nil, err
}
return read(ctx, client)
for {
Copy link
Owner

Choose a reason for hiding this comment

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

why a looping here ?

The ActionId, in case of included in the command, must have to be in the response.
I'd like to suggest returning a known error to be thread outside the library, for example:

var (
  // ErrInvalidResponseActionID occurss when the response doesn't have the same action id
  ErrInvalidResponseActionID = errors.New("invalid response action id")
)

...
        response, err := read(ctx, client)
        if err != nil {
                return nil, err
        }
        if id == "" || id == response.Get("ActionID") {
                return response, err
        }
        return nil, ErrInvalidResponseActionID

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the AMI interface is asynchronous. the response returned by the "read" function may not be the response to the executed command, but some asynchronous event returned. An example of this is the Logout command, when you run it you will receive two messages from AMI, the response to the command and a "logout" event, depending on the order in which the messages arrives will fail.
the only way I could think of to avoid this is with a loop, waiting for the response to the actionid, but it makes the actionID mandatory

ami/utils.go Outdated Show resolved Hide resolved
ami/utils.go Outdated Show resolved Hide resolved
@mind-ar mind-ar marked this pull request as draft November 14, 2021 19:51
@mind-ar mind-ar marked this pull request as ready for review November 14, 2021 20:10
@mind-ar mind-ar closed this Nov 14, 2021
@mind-ar mind-ar deleted the fix-action-id branch November 14, 2021 20:26
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.

Possible async events not handle. Logoff inconsistant behaviour
2 participants