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

Semi-RFC: Consolidate http behavior #272

Closed
wants to merge 8 commits into from

Conversation

tiedotguy
Copy link
Collaborator

This is the next change in the whole transport refactor saga, leading towards streaming (#10, #84, #159), InfluxDB (#14), and clustering. Nothing here is consumed currently, it's just the primitives. As it's getting rather large already, I wanted to minimize the review size. Commits are relatively stand-alone.

This PR is not expected to be merged yet, because there's documentation mismatches (due to lack of consumption). I'll be working on updating consumers in a subsequent PR, but didn't want to get too deep in to that in case there's feedback on the API (I'm not a huge fan of the TransportOptions for example)


// Base headers
req.Header.Set("Content-Type", contentType)
req.Header.Set("Content-Encoding", encoding)
Copy link
Contributor

Choose a reason for hiding this comment

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

Encoding and type could be enums for a little bit more type safety. Currently there is a risk to pass two strings in swapped positions.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Swapping is definitely a hazard, but I don't know how to prevent it. Go lacks enums (near as I can tell), and will happily promote a string to an alias of it, breaking the type safety: https://play.golang.org/p/lzAsJ8gGB_O

I could do a sanity check for valid values, but that seems overkill. Is there something I'm missing?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, what we are seeing in that example is the behavior of "untyped literals". This does not compile though because x is typed vs untyped literal. https://play.golang.org/p/wiCUwuQqj5g

Copy link
Collaborator Author

@tiedotguy tiedotguy Oct 25, 2019

Choose a reason for hiding this comment

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

Ahh! I see now. https://play.golang.org/p/xItV45OW49V

I'll fix it up in the cleanup commit (waiting on other feedback, I know the team is busy atm)

  • this

Copy link
Contributor

Choose a reason for hiding this comment

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

https://blog.golang.org/constants see the String constants section

if err != nil {
return nil, err
}
json.ReturnStream(stream)
Copy link
Contributor

Choose a reason for hiding this comment

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

So if there was an error we don't return the stream? This line should be after Flush?

Copy link
Collaborator Author

@tiedotguy tiedotguy Oct 25, 2019

Choose a reason for hiding this comment

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

Good catch!

  • this

backoff, err := util.GetRetryFromViper(sub)
if err != nil {
tp.logger.WithField("name", name).WithError(err).Error("failed to load retry policy")
return nil, err
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason to both log and return the error? Ideally should only return it

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not specifically. Depending on the call stack it could actually end up dropping a few duplicate logs - they're all fatal though (fail early), so it's something I didn't put a lot of thought in to.

For preference I'd actually make it a .Fatal(). I've been slowly pushing to remove errors from the code (eg, this whole refactor is fire and forget) with a view to either fail-fast on startup, or handle runtime errors immediately when there's maximum context available.

Ultimately a lot of the error paths just pop out somewhere high level with a log line, and they lose context that could be supplied by an immediate log (such as the transport name in this case).

There's an argument to be made for more strongly typed errors to provide context, but a) I'm lazy, and b) the only way to expose that context is through error.Error() which means it's not exposed as structured log data.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, I see your point. I think this code can be improved further if config parsing is moved into NewTransportPool() and viper is no longed referenced/used. I don't know if it's easy to do this. Also, just terminating the program in the middle of the pooling code would be weird. Testing and reusing this code becomes harder.

@@ -80,16 +139,37 @@ func (tp *TransportPool) newClient(name string) (*Client, error) {
return nil, err
}

headers := map[string]string{}
headerKeys := []string{}
Copy link
Contributor

Choose a reason for hiding this comment

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

Could pre-allocate the exact size for both

Copy link
Collaborator Author

@tiedotguy tiedotguy Oct 25, 2019

Choose a reason for hiding this comment

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

  • this

retryPolicy := v.GetString(paramRetryPolicy)

if retryInterval <= 0 {
return nil, errors.New(paramRetryInterval + " must be positive")
Copy link
Contributor

Choose a reason for hiding this comment

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

Could use fmt.Errorf() here and below for consistency with other places where things are not strings. Not a big deal.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

My main thought was it should turn it in to a static string at compile time rather than runtime. I don't know if that will actually occur with either "" + "" or fmt.Sprintf("%s", ""), but that was the intent.

"github.com/stretchr/testify/require"
)

func TestSemaphoreUnlimited(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

t.Parallel()

Copy link
Collaborator Author

@tiedotguy tiedotguy Oct 25, 2019

Choose a reason for hiding this comment

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

  • this

@tiedotguy
Copy link
Collaborator Author

Rebased off master, fixed the issues mentioned. Last commit is a bit derpy because I did a fixup instead of reword when cleaning up the "address issues from review" commit.

This adds a richer retry policy framework configurable through Viper, allowing
more centralisation and consistency.
There's also a null semaphore which lets anything in, which can be
used to provide unbound access without extra logic.
@MovieStoreGuy
Copy link
Contributor

Hey @tiedotguy , you currently have a merge conflict on this, would you be able to resolve this?

@tiedotguy
Copy link
Collaborator Author

No merge conflicts, simply happy accidents.

This branch will return later.

@tiedotguy tiedotguy closed this Jul 7, 2020
@MovieStoreGuy MovieStoreGuy reopened this Jul 7, 2020
@tiedotguy tiedotguy deleted the consolidate-http-behavior branch October 5, 2020 02:45
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.

3 participants