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

Add unique request id #292

Closed
wants to merge 2 commits into from
Closed

Add unique request id #292

wants to merge 2 commits into from

Conversation

bkmit
Copy link
Contributor

@bkmit bkmit commented May 12, 2017

This patch add proxy.header.uniquerequestid configuration option for
specifying header name for adding unique ID to each http request proxied by
fabio.
This header value can be logged in fabio access log and used for request
processing tracking across different applications.

@CLAassistant
Copy link

CLAassistant commented May 12, 2017

CLA assistant check
All committers have signed the CLA.

Copy link
Contributor

@magiconair magiconair left a comment

Choose a reason for hiding this comment

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

I think this is a good idea. A couple of comments:

  • I don't see the code that handles the access logging.
  • Please update the NOTICES.txt file for each vendored library.

However, unless the unique request id format is fully configurable and pluggable I'd prefer if you just write a function which generates a UUID instead of pulling the library in for that one function.

"X-Forwarded-Proto": []string{"http"},
"X-Forwarded-Port": []string{"1234"},
"X-Real-Ip": []string{"1.2.3.4"},
"X-Unique-Request-Id": []string{""}, // XXX nedd to figure out how to regexp match
Copy link
Contributor

Choose a reason for hiding this comment

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

You add a func UUID() to the proxy which you can mock.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Made changes you suggest.

Copy link
Contributor Author

@bkmit bkmit May 12, 2017

Choose a reason for hiding this comment

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

Logging can be made by using $header.ConfiguredHeaderName format already present in fabio.

fabio.properties Outdated
@@ -353,6 +353,13 @@
# proxy.header.tls.value =


# proxy.header.uniquerequesti configures the header for the adding unique request id.
Copy link
Contributor

Choose a reason for hiding this comment

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

typo

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

config/config.go Outdated
@@ -57,6 +57,7 @@ type Proxy struct {
TLSHeader string
TLSHeaderValue string
GZIPContentTypes *regexp.Regexp
UniqueRequestID string
Copy link
Contributor

Choose a reason for hiding this comment

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

I think RequestID is enough since they need to be unique for this to make sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Shortened option name

Copy link
Contributor

@magiconair magiconair left a comment

Choose a reason for hiding this comment

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

Can you move the UUID code into uuid/uuid.go and put into the header a copyright notice where you copied this from, please? Also, update the NOTICES.txt file with a reference to that project and the license needs to be compatible (MIT, BSD, ...).

@magiconair
Copy link
Contributor

you're also correct with the logging.

@magiconair magiconair changed the title This patch add uniquerequestid configuration option. Add unique request id May 12, 2017
@magiconair
Copy link
Contributor

Almost there. For testing I suggest you add a UUID function to the proxy similar to the Time function:

// UUID returns a unique id in uuid format.
// If UUID is nil, uuid.NewV4() is used.
UUID func() string

Then create in the uuid package:

func NewV4() string {
    return EncodeHex(NewRandom())
}

Now you can mock this in the test.

@magiconair
Copy link
Contributor

One thing I'd like to know though and maybe you can add a benchmark for this is how many UUIDs this approach can generate? Is this using a pseudo random number generator which can generate enough entropy for thousands of req/sec or will this become a limiting factor for the throughput? If yes, then we need to find a better way.

@magiconair
Copy link
Contributor

There are users with 20k req/sec (or more) on a single instance. I won't name you in public but you know who you are :)

@aaronhurt
Copy link
Member

aaronhurt commented May 12, 2017

Think I found a winner ... it's not RFC 1422 compliant but it does generate unique IDs and it does it very fast. The other standard UUID packages around github do won't meet the 20k/s request requirement.

Code

package main

import (
	"fmt"
	"testing"

	guuid "github.com/google/uuid"
	muuid "github.com/m4rw3r/uuid"
	fuuid "github.com/rogpeppe/fastuuid"
	suuid "github.com/satori/go.uuid"
)

func BenchmarkV4Google(b *testing.B) {
	for i := 0; i < b.N; i++ {
		guuid.NewRandom()
	}
}

func BenchmarkV4Satori(b *testing.B) {
	for i := 0; i < b.N; i++ {
		suuid.NewV4()
	}
}

func BenchmarkV4m4rw3r(b *testing.B) {
	for i := 0; i < b.N; i++ {
		muuid.V4()
	}
}

func BenchmarkFastUUIDRaw(b *testing.B) {
	g := fuuid.MustNewGenerator()
	for i := 0; i < b.N; i++ {
		g.Next()
	}
}

func BenchmarkFastUUIDFormatted(b *testing.B) {
	g := fuuid.MustNewGenerator()
	for i := 0; i < b.N; i++ {
		_ = fmt.Sprintf("%x", g.Next())
	}
}

Results

ahurt1:uuid-test ahurt$ go test -bench . -benchtime 10s
BenchmarkV4Google-4            	10000000	      1292 ns/op
BenchmarkV4Satori-4            	10000000	      1272 ns/op
BenchmarkV4m4rw3r-4            	10000000	      1285 ns/op
BenchmarkFastUUIDRaw-4         	1000000000	        20.0 ns/op
BenchmarkFastUUIDFormatted-4   	30000000	       505 ns/op
PASS
ok  	github.com/leprechau/uuid-test	80.111s
ahurt1:uuid-test ahurt$

Even with the formatting being done by the relatively heavy fmt package the fastuuid package is the clear winner and would exceed the required performance metric.

@magiconair
Copy link
Contributor

@leprechau very cool. Thx for digging. Want to add a fast formatter? That lib is also small enough to be vendored in.

@aaronhurt
Copy link
Member

@magiconair Done. I put the formatter and test here: https://github.com/leprechau/uuid-test
@bkmit Feel free to use/adopt as needed.

@bkmit
Copy link
Contributor Author

bkmit commented May 12, 2017

Wow! Great research! I'll definitely use fastest library.

@bkmit
Copy link
Contributor Author

bkmit commented May 16, 2017

@magiconair Do I need to make some more improvements?

@magiconair
Copy link
Contributor

Sorry. Got sidetracked. Looking now.

Copy link
Contributor

@magiconair magiconair left a comment

Choose a reason for hiding this comment

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

Some small changes but other than that this looks quite good!

Could you add an integration test as well? You can either copy the TestProxyProducesCorrectXffHeader or refactor/rename it slightly so that it tests for more than one header. I'd like to make sure that this doesn't get lost during some refactoring.

fabio.properties Outdated
@@ -353,6 +353,13 @@
# proxy.header.tls.value =


# proxy.header.requestid configures the header for the adding request id.
Copy link
Contributor

Choose a reason for hiding this comment

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

proxy.header.requestid configures the header for the adding a unique request id.

// addRequestID Set header with name from `cfg.RequestID` to UUID value
//
func addRequestID(r *http.Request, p *HTTPProxy) {
if p.Config.RequestID != "" {
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 add this to the addHeaders() function and move your test to the TestAddHeaders() fn as well, please? This isn't complex enough for a separate fn/test IMO.

if p.Config.RequestID != "" {
    id := p.UUID
    if id == nil {
        id = uuid.NewUUID
    }
    r.Header.Set(p.Config.RequestID, id())
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have did it in separate function because it need HTTPProxy, which is not available in addHeaders.
Changing addHeaders signature lead to massive change of it unit tests.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see. Then just inline the couple lines into the HTTP proxy and use the integration test to test it.

@magiconair
Copy link
Contributor

Looks good. One last request. Can you split this up into just two commits? One for the vendored in library and one for the feature change? I don't want to squash those two.

@magiconair
Copy link
Contributor

Once you're done pls force push the change so that it replaces the current list of commits.

bkmit added 2 commits May 16, 2017 15:19
specifying header name for adding unique ID to each http request proxied by
fabio.
This header value can be logged in fabio access log and used for request
processing tracking across different request handling applications.
magiconair pushed a commit that referenced this pull request May 16, 2017
This patch add proxy.header.requestid configuration option
for specifying header name for adding unique ID to each http
request proxied by fabio.

This header value can be logged in fabio access log and used
for request processing tracking across different request
handling applications.
@magiconair
Copy link
Contributor

merged it.

@magiconair magiconair closed this May 16, 2017
@magiconair
Copy link
Contributor

@bkmit Thanks for your patience :)

@bkmit
Copy link
Contributor Author

bkmit commented May 16, 2017

@magiconair Many thanks for your patience! This was my first public golang development (micro) project!

@magiconair
Copy link
Contributor

For that the quality was quite high! Hats off.

@magiconair magiconair added this to the 1.5.0 milestone Oct 10, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants