-
Notifications
You must be signed in to change notification settings - Fork 12
Avoid no-op EC KV reads in post-send pull sync #900
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ use super::kv::{ | |
| }; | ||
| use super::kv_types::KvEntry; | ||
| use super::prebid_eids::collect_eid_cookie_updates; | ||
| use super::pull_sync_marker::{expire_marker, reconcile_marker}; | ||
| use super::registry::PartnerRegistry; | ||
| use super::{EcKvSnapshot, current_timestamp, log_id}; | ||
|
|
||
|
|
@@ -52,10 +53,17 @@ pub fn ec_finalize_response( | |
| sharedid_cookie: Option<&str>, | ||
| response: &mut Response<EdgeBody>, | ||
| ) { | ||
| ec_context.validate_pull_sync_marker(settings, registry); | ||
| let consent_allows_ec = ec_consent_granted(ec_context.consent()); | ||
| let consent_withdrawn = ec_consent_withdrawn(ec_context.consent()); | ||
|
|
||
| if !consent_allows_ec { | ||
| // Expire the request-local marker independently of the EC cookie: a | ||
| // withdrawal must stop any pending pull-sync disclosure window. | ||
| if consent_withdrawn && ec_context.pull_sync_marker().was_present() { | ||
| expire_marker(ec_context.pull_sync_marker_mut(), response); | ||
| } | ||
|
|
||
| finalize_unusable_consent( | ||
| settings, | ||
| ec_context, | ||
|
|
@@ -86,6 +94,8 @@ pub fn ec_finalize_response( | |
| } | ||
| } | ||
|
|
||
| reconcile_pull_sync_marker(settings, registry, ec_context, response); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔧 wrench — Orphan recovery silently stops firing on the marker-skip path. The PR summary says snapshot reads required by "orphan recovery" are preserved. They aren't. Recovery is gated four lines above this one on the snapshot being if matches!(ec_context.kv_snapshot(), EcKvSnapshot::Missing { .. })
&& ec_context.recovery_eligible()On the marker-skip path the preload never runs, so the snapshot is I verified this rather than inferring it. Taking the existing The orphan is not rotated and no replacement cookie is issued. With Two things make this worse than a one-request delay:
The direct fix is a fifth hatch: // in EcSnapshotPreloadInput
recovery_eligible: bool,
// in should_preload_ec_snapshot
let marker_can_skip = input.marker_valid
&& !input.auction_needs_row
&& !input.eid_cookie_may_need_persistence
&& !input.privacy_needs_row
&& !input.recovery_eligible
&& !input.snapshot_already_read;But note that Apply manually — can't be auto-applied as a suggestion because the fix spans the struct definition, the gate body, and the call site across two separate hunks. |
||
|
|
||
| // Ordinary returning-user page views no longer refresh the browser | ||
| // cookie, emit the EC header, or update KV TTL. | ||
| return; | ||
|
|
@@ -97,6 +107,7 @@ pub fn ec_finalize_response( | |
| if ec_context.ec_generated() { | ||
| let (Some(graph), Some(ec_id)) = (kv, ec_context.ec_value().map(str::to_owned)) else { | ||
| log::info!("Skipping generated EC response write because KV graph is unavailable"); | ||
| reconcile_pull_sync_marker(settings, registry, ec_context, response); | ||
| return; | ||
| }; | ||
|
|
||
|
|
@@ -113,6 +124,26 @@ pub fn ec_finalize_response( | |
| log::warn!("Skipping generated EC cookie because backing row is not authoritative"); | ||
| } | ||
| } | ||
|
|
||
| reconcile_pull_sync_marker(settings, registry, ec_context, response); | ||
| } | ||
|
|
||
| fn reconcile_pull_sync_marker( | ||
|
ChristianPavilonis marked this conversation as resolved.
|
||
| settings: &Settings, | ||
| registry: &PartnerRegistry, | ||
| ec_context: &mut EcContext, | ||
| response: &mut Response<EdgeBody>, | ||
| ) { | ||
| let ec_id = ec_context.ec_value().map(str::to_owned); | ||
| let snapshot = ec_context.kv_snapshot().clone(); | ||
| reconcile_marker( | ||
| settings, | ||
| registry, | ||
| ec_id.as_deref(), | ||
| &snapshot, | ||
| ec_context.pull_sync_marker_mut(), | ||
| response, | ||
| ); | ||
| } | ||
|
|
||
| fn recover_orphaned_ec( | ||
|
|
@@ -1523,6 +1554,132 @@ mod tests { | |
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn finalize_sets_marker_for_complete_pull_partner_snapshot() { | ||
| let settings = create_test_settings(); | ||
| let ec_id = sample_ec_id("compl1"); | ||
| let mut partner = make_partner("ssp.example.com"); | ||
| partner.pull_sync_enabled = true; | ||
| partner.pull_sync_url = Some("https://sync.example.com/pull".to_owned()); | ||
| partner.pull_sync_allowed_domains = vec!["sync.example.com".to_owned()]; | ||
| partner.ts_pull_token = Some(Redacted::new("pull-token".to_owned())); | ||
| let registry = PartnerRegistry::from_config(&[partner]).expect("should build registry"); | ||
| let mut ec_context = make_context( | ||
| Some(&ec_id), | ||
| Some(&ec_id), | ||
| true, | ||
| false, | ||
| Jurisdiction::NonRegulated, | ||
| ); | ||
| let mut entry = live_entry(); | ||
| entry.ids.insert( | ||
| "ssp.example.com".to_owned(), | ||
| crate::ec::kv_types::KvPartnerId { | ||
| uid: "partner-uid".to_owned(), | ||
| }, | ||
| ); | ||
| ec_context.set_kv_snapshot(EcKvSnapshot::Present { | ||
| ec_id, | ||
| entry: Box::new(entry), | ||
| generation: Some(1), | ||
| }); | ||
| let mut response = empty_response(); | ||
|
|
||
| ec_finalize_response( | ||
| &settings, | ||
| &mut ec_context, | ||
| None, | ||
| ®istry, | ||
| None, | ||
| None, | ||
| &mut response, | ||
| ); | ||
|
|
||
| let cookies = response | ||
| .headers() | ||
| .get_all(http::header::SET_COOKIE) | ||
| .iter() | ||
| .filter_map(|value| value.to_str().ok()) | ||
| .collect::<Vec<_>>(); | ||
| assert!( | ||
| cookies | ||
| .iter() | ||
| .any(|cookie| cookie.starts_with("ts-ec-pull-complete=v1.")), | ||
| "complete snapshot should issue the marker" | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn explicit_withdrawal_without_marker_or_ec_cookie_does_not_set_cookie() { | ||
| let settings = create_test_settings(); | ||
| let consent = ConsentContext { | ||
| jurisdiction: Jurisdiction::UsState("CA".to_owned()), | ||
| gpc: true, | ||
| source: ConsentSource::Cookie, | ||
| ..Default::default() | ||
| }; | ||
| let mut ec_context = make_context_with_consent(None, None, false, false, consent); | ||
| let mut response = empty_response(); | ||
|
|
||
| ec_finalize_response( | ||
| &settings, | ||
| &mut ec_context, | ||
| None, | ||
| &PartnerRegistry::empty(), | ||
| None, | ||
| None, | ||
| &mut response, | ||
| ); | ||
|
|
||
| assert!( | ||
| response.headers().get(http::header::SET_COOKIE).is_none(), | ||
| "withdrawal without browser identity state should not add a cookie" | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn explicit_withdrawal_expires_marker_without_ec_cookie() { | ||
| let settings = create_test_settings(); | ||
| let consent = ConsentContext { | ||
| jurisdiction: Jurisdiction::UsState("CA".to_owned()), | ||
| gpc: true, | ||
| source: ConsentSource::Cookie, | ||
| ..Default::default() | ||
| }; | ||
| let mut ec_context = make_context_with_consent(None, None, false, false, consent); | ||
| ec_context.set_pull_sync_marker_for_test( | ||
| crate::ec::pull_sync_marker::PullSyncMarkerState::Invalid, | ||
| ); | ||
| let mut response = empty_response(); | ||
|
|
||
| ec_finalize_response( | ||
| &settings, | ||
| &mut ec_context, | ||
| None, | ||
| &PartnerRegistry::empty(), | ||
| None, | ||
| None, | ||
| &mut response, | ||
| ); | ||
|
|
||
| let cookies = response | ||
| .headers() | ||
| .get_all(http::header::SET_COOKIE) | ||
| .iter() | ||
| .filter_map(|value| value.to_str().ok()) | ||
| .collect::<Vec<_>>(); | ||
| assert!( | ||
| cookies.iter().any(|cookie| { | ||
| cookie.starts_with("ts-ec-pull-complete=;") && cookie.contains("Max-Age=0") | ||
| }), | ||
| "withdrawal should expire the marker independently of EC cookie state" | ||
| ); | ||
| assert!( | ||
| cookies.iter().all(|cookie| !cookie.starts_with("ts-ec=;")), | ||
| "missing EC cookie should not add an EC-cookie expiry" | ||
| ); | ||
| } | ||
|
|
||
| fn live_entry() -> KvEntry { | ||
| let mut entry = KvEntry::tombstone(1000); | ||
| entry.consent.ok = true; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.