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

/cookie_sync Endpoint Rewrite #1879

Merged
merged 65 commits into from
Aug 19, 2021
Merged

Conversation

SyntaxNode
Copy link
Contributor

@SyntaxNode SyntaxNode commented Jun 5, 2021

I've rewritten the majority of the /cookie_sync endpoint and user syncer architecture to implement #924, #1477, #1554, and to further progress #1429 as the config system now includes user syncing settings.

Todo

  • Merge from master
  • Add config tests
  • Standardize metrics and add unit tests.
  • Convert all existing user sync configs to the new format.

Configuration Changes

Technical details will be included in a separate PR to https://github.com/prebid/prebid.github.io before this is merged.

  • User sync configuration is relocated to the bidder yaml files in static/bidder-info from being hardcoded in the config.go and adapter usersync.go files. Hosts can now override all user syncing options via either pbs.yaml or environment variables.
  • User sync configuration now supports new macros, automatic url encoding, and a new configuration hierarchy. The external url may now be overridden at the host level, the bidder level, and the endpoint level. The default redirect url is now represented with macros such that no modification is needed for most cases. Look for more details in the docs PR.
  • Existing usersync_url overrides are temporarily supported for backwards compatibility, but please update your configs as soon as possible after release.
  • Bidders may share the same user sync key, this is ideal for hardcoded aliases or bidders with multiple adapter implementations. As a side effect, I'm delighted to announce this removes the special adnxs hardcoded handling for AppNexus.
  • Bidders may now specify both iframe and/or redirect endpoints.
  • Cooperative cookie syncing is implemented and disabled by default.

Example Current Config: Buried In config/config.go

setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAppnexus, "https://ib.adnxs.com/getuid?"+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadnxs%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")

Example New Config: In static/bidder-info/appnexus.yaml

userSync:
  key: "adnxs"
  redirect:
    url: "https://ib.adnxs.com/getuid?{{.RedirectURL}}"
    userMacro: "$UID"

API Changes

  • The /cookie_sync endpoint now defaults to all bidders if the bidders field in the request is missing, null, or empty. This matches behavior in PBS-Java. We discussed and couldn't find a use case for handling each of these conditions separately. Currently, a missing bidders parameter results in an error response, a null parameter results in using all bidders, and an empty parameter results in no user syncing.
  • The /cookie_sync endpoint will now accept filters in the request defining which user sync types (iframe, redirect) are permitted. The format is fully compatible with Prebid.js and PBS-Java.
  • Cooperative cookie syncing is now supported. Requests may specify if they want this feature enabled or disabled.
  • The SSCookie sidecar cookie is no longer required and is removed with this PR.
  • PBS will no longer invalidate user syncs when it detects the browser has upgraded to a new version with support for same site cookies. This behavior isn't present in PBS-Java and our usage data shows the vast majority of end users are already using same site cookie compatible browsers.
  • The /setuid endpoint will now properly respond with an empty image for redirects and an empty html response for iframes.

Metric Changes

Influx

Metrics have be reorganized around endpoint names and syncer keys. cookie_sync_requests.* are directly related to the /cookie_sync endpoint, set_uid_requests.* are directly related to the /setuid endpoint, and syncer.* are related to each syncer. Specifically,

  • usersync.opt_outs and usersync.bad_requests are removed and replaced with cookie_sync_requests.[status] where status is ok, bad_request, opt_out, and gdpr_blocked_host_cookie.
  • usersync.unknown.sets and usersync.unknown.gdpr_prevent are removed and replaced with set_uid_requests.key_unknown.
  • cookie_syncs[bidder].gen and cookie_sync.[bidder].gdpr_prevent are removed and replaced with syncer.[key].request.[status] where the syncer key may be shared among multiple bidders and status is ok, privacy_blocked, already_synced, type_not_supported.
  • usersync.[bidder].sets and usersync.[bidder].gdpr_prevent are removed and replaced with syncer.[key].set.[status] where the syncer key may be shared among multiple bidders and status is ok and privacy_blocked.
  • setuid_requests.[status] is new where status is ok, opt_out, gdpr_blocked_host_cookie, key_unknown, and type_not_supported.

Prometheus

Metrics have be reorganized around endpoint names and syncer keys. cookie_sync_requests is directly related to the /cookie_sync endpoint, set_uid_requests is directly related to the /setuid endpoint, syncer_requests and syncer_sets are related to each syncer. Specifically,

  • cookie_sync_request now has a status label with values ok, bad_request, opt_out, and gdpr_blocked_host_cookie. Existing PromQL queries will still accurately report the total number of requests to the endpoint.
  • setuid_requests is new and has a status label with values ok, opt_out, gdpr_blocked_host_cookie, key_unknown, and type_not_supported.
  • adapter_cookie_sync has been removed and replaced with syncer_requests which has a syncer key label and status label with the values ok, privacy_blocked, already_synced, type_not_supported. These syncer metrics are only counted when the syncer has been selected as a user sync candidate.
  • adapter_user_sync has been removed and replaced with syncer_sets which has a syncer key label and status label with the values ok and privacy_blocked.

Future Plans

I'm working on account settings for the /cookie_sync endpoint, but this PR is already huge so that will be separate later on.

hhhjort
hhhjort previously approved these changes Aug 11, 2021
Copy link
Collaborator

@hhhjort hhhjort left a comment

Choose a reason for hiding this comment

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

LGTM

bsardo
bsardo previously approved these changes Aug 12, 2021
Copy link
Collaborator

@bsardo bsardo left a comment

Choose a reason for hiding this comment

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

LGTM

@SyntaxNode SyntaxNode dismissed stale reviews from bsardo and hhhjort via 7d3bbb5 August 12, 2021 18:12
}

errs := validateSyncers(t, bidderInfos)
assert.Empty(t, errs, "syncer errors")
}
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 improved the unit tests to use the same exact loading mechanism the app uses at startup to ensure no issues / the same validation is performed.


// a syncer may provide just a Supports field to provide hints to the host. we should only try to create a syncer
// if there is at least one non-Supports value populated.
return cfg.Syncer.Key != "" || cfg.Syncer.Default != "" || cfg.Syncer.IFrame != nil || cfg.Syncer.Redirect != nil || cfg.Syncer.SupportCORS != nil
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a bit hacky. Perhaps it would be better to use different data structures for yaml vs config overrides... but I think this works.

givenConfig: hostConfig,
givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAEmpty, "bidder2": infoKeyAError},
expectedErrors: []string{
"cannot create syncer for bidder bidder2 with key a: default is set to redirect but no redirect endpoint is configured",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just to confirm, bidder1 does not result in an error here even though it is missing endpoints and a default because it shares the same key as bidder2 and bidder2 is supposed to define the endpoints for that key since it specifies a default right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct. Exactly. I wrote it to cover the case where two bidders share the same key but the primary configuration (bidder2 here) is invalid.

_, err = syncer.GetSync([]usersync.SyncType{usersync.SyncTypeIFrame, usersync.SyncTypeRedirect}, privacyPolicies)
if err != nil {
return fmt.Errorf("syncer has invalid macro: %s", err)
for _, v := range bidderInfo.Syncer.Supports {
Copy link
Collaborator

Choose a reason for hiding this comment

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

You're leaning on the app validation performed in BuildSyncers in your tests but then we have this additional test only check here that makes sure we don't have an invalid supports type in the bidder info file. Shouldn't we be checking for invalid support types within our app validation too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. It was my intention to emit a warning for supported user syncs which aren't configured. I've added that feature with the supports validation.

assert.Equal(t, test.expectedBidderInfos, test.givenBidderInfos, test.description+":result")
} else {
assert.EqualError(t, resultErr, test.expectedError, test.description+":err")
}
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 realize that I'm not asserting the glog warnings. That's because we don't have a good way to intercept the log writes for tests. I'd like to see that fixed, but that would be too much work for this PR. I manually verified for now.

switch endpointLower {
case "iframe":
if info.Syncer.IFrame == nil {
glog.Warningf("bidder %s supports iframe user sync, but doesn't have a default and must be configured by the host", name)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just wanted to confirm that you don't feel a need to test for the warning. You do test for the error below.

Copy link
Contributor Author

@SyntaxNode SyntaxNode Aug 18, 2021

Choose a reason for hiding this comment

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

I added a comment about that lack of coverage a little while ago.

I'd really like to test the warning, but we don't have an interface for logging and intercepting glog through output redirection isn't a good option. I think adding that logging interface is too much to do here, so I leave it for a future project. There are many other glog warnings we equally have no automated test coverage on. I ran through the test cases manually to ensure the proper glog output.

I also thought of using warnings and errors as returns from the method instead using our existing errortypes functionality, but then I need to filter them out in the router and it seemed to be getting messy IMHO.

Happy to add the assertions if you have an idea for how to reasonable approach it.

bsardo
bsardo previously approved these changes Aug 18, 2021
Copy link
Collaborator

@bsardo bsardo left a comment

Choose a reason for hiding this comment

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

LGTM

hhhjort
hhhjort previously approved these changes Aug 18, 2021
Copy link
Collaborator

@hhhjort hhhjort left a comment

Choose a reason for hiding this comment

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

LGTM

@SyntaxNode
Copy link
Contributor Author

Docs PR: prebid/prebid.github.io#3220

@SyntaxNode SyntaxNode dismissed stale reviews from hhhjort and bsardo via 18ea3ca August 19, 2021 17:35
@SyntaxNode SyntaxNode merged commit 6e0b3de into prebid:master Aug 19, 2021
@SyntaxNode SyntaxNode mentioned this pull request Aug 20, 2021
wwwyyy pushed a commit to wwwyyy/prebid-server that referenced this pull request Aug 20, 2021
… prebid-master

* 'master' of https://github.com/prebid/prebid-server:
  Update Viper (prebid#1969)
  /cookie_sync Endpoint Rewrite (prebid#1879)
  New Adapter: IQZone (prebid#1964)
  Remove old race conidtion tests (prebid#1958)
  Remove time.Now() as the first parameter of NewRates() (prebid#1953)

# Conflicts:
#	config/config.go
#	usersync/usersyncers/syncer_test.go
@SyntaxNode SyntaxNode deleted the usersync_v2 branch September 2, 2021 16:37
jizeyopera pushed a commit to operaads/prebid-server that referenced this pull request Oct 13, 2021
shunj-nb pushed a commit to ParticleMedia/prebid-server that referenced this pull request Nov 8, 2022
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.

5 participants