@@ -42,6 +42,12 @@ public class UidsCookieService {
4242 private static final int MIN_NUMBER_OF_UID_COOKIES = 1 ;
4343 private static final int MAX_NUMBER_OF_UID_COOKIES = 30 ;
4444
45+ // {"tempUIDs":{},"optout":false}
46+ private static final int TEMP_UIDS_BASE64_BYTES = "eyJ0ZW1wVUlEcyI6e30sIm9wdG91dCI6ZmFsc2V9" .length ();
47+ // "":{"uid":"","expires":"1970-01-01T00:00:00.000000000Z"},
48+ private static final int UID_BASE64_BYTES = ("IiI6eyJ1aWQiOiIiLCJleHBpcmVzI"
49+ + "joiMTk3MC0wMS0wMVQwMDowMDowMC4wMDAwMDAwMDBaIn0s" ).length ();
50+
4551 private final String optOutCookieName ;
4652 private final String optOutCookieValue ;
4753 private final String hostCookieFamily ;
@@ -163,26 +169,24 @@ private Uids parseUids(Map<String, String> cookies) {
163169 * as a value.
164170 */
165171 public Cookie aliveCookie (String cookieName , UidsCookie uidsCookie ) {
166- return Cookie
167- .cookie (cookieName , Base64 .getUrlEncoder ().encodeToString (uidsCookie .toJson ().getBytes ()))
168- .setPath ("/" )
169- .setSameSite (CookieSameSite .NONE )
170- .setSecure (true )
171- .setMaxAge (ttlSeconds )
172- .setDomain (hostCookieDomain );
172+ final String value = Base64 .getUrlEncoder ().encodeToString (uidsCookie .toJson ().getBytes ());
173+ return makeCookie (cookieName , value , ttlSeconds );
173174 }
174175
175176 public Cookie aliveCookie (UidsCookie uidsCookie ) {
176177 return aliveCookie (COOKIE_NAME , uidsCookie );
177178 }
178179
179180 public Cookie expiredCookie (String cookieName ) {
180- return Cookie
181- .cookie (cookieName , StringUtils .EMPTY )
181+ return makeCookie (cookieName , StringUtils .EMPTY , 0 );
182+ }
183+
184+ private Cookie makeCookie (String cookieName , String value , long maxAge ) {
185+ return Cookie .cookie (cookieName , value )
182186 .setPath ("/" )
183187 .setSameSite (CookieSameSite .NONE )
184188 .setSecure (true )
185- .setMaxAge (0 )
189+ .setMaxAge (maxAge )
186190 .setDomain (hostCookieDomain );
187191 }
188192
@@ -283,33 +287,38 @@ public List<Cookie> splitUidsIntoCookies(UidsCookie uidsCookie) {
283287 final Iterator <String > cookieFamilies = cookieFamilyNamesByDescPriorityAndExpiration (uidsCookie );
284288 final List <Cookie > splitCookies = new ArrayList <>();
285289
286- int uidsIndex = 0 ;
290+ final int staticCookieDataBytes = makeCookie (COOKIE_NAME , StringUtils .EMPTY , ttlSeconds ).encode ().length ();
291+
287292 String nextCookieFamily = null ;
288293
289- while (uidsIndex < numberOfUidCookies ) {
290- final String uidsName = uidsIndex == 0 ? COOKIE_NAME : COOKIE_NAME_FORMAT .formatted (uidsIndex + 1 );
291- UidsCookie tempUidsCookie = new UidsCookie (
292- Uids .builder ().uids (new HashMap <>()).optout (hasOptout ).build (),
293- mapper );
294+ for (int uidsIndex = 0 ; uidsIndex < numberOfUidCookies ; uidsIndex ++) {
295+ int actualCookieSize = staticCookieDataBytes + TEMP_UIDS_BASE64_BYTES ;
296+ final Map <String , UidWithExpiry > tempUids = new HashMap <>();
294297
295298 while (nextCookieFamily != null || cookieFamilies .hasNext ()) {
296299 nextCookieFamily = nextCookieFamily == null ? cookieFamilies .next () : nextCookieFamily ;
297- tempUidsCookie = tempUidsCookie .updateUid (nextCookieFamily , uids .get (nextCookieFamily ));
298- if (cookieExceededMaxLength (uidsName , tempUidsCookie )) {
299- tempUidsCookie = tempUidsCookie .deleteUid (nextCookieFamily );
300+
301+ final UidWithExpiry uidWithExpiry = uids .get (nextCookieFamily );
302+ actualCookieSize += UID_BASE64_BYTES
303+ + calculateCookieSize (uidsIndex , nextCookieFamily , uidWithExpiry .getUid ());
304+
305+ if (maxCookieSizeBytes > 0 && actualCookieSize > maxCookieSizeBytes ) {
300306 break ;
301307 }
302308
309+ tempUids .put (nextCookieFamily , uidWithExpiry );
303310 nextCookieFamily = null ;
304311 }
305312
306- if (tempUidsCookie .getCookieUids ().getUids ().isEmpty ()) {
313+ final String uidsName = uidsIndex == 0 ? COOKIE_NAME : COOKIE_NAME_FORMAT .formatted (uidsIndex + 1 );
314+
315+ if (tempUids .isEmpty ()) {
307316 splitCookies .add (expiredCookie (uidsName ));
308317 } else {
309- splitCookies .add (aliveCookie (uidsName , tempUidsCookie ));
318+ splitCookies .add (aliveCookie (
319+ uidsName ,
320+ new UidsCookie (Uids .builder ().uids (tempUids ).optout (hasOptout ).build (), mapper )));
310321 }
311-
312- uidsIndex ++;
313322 }
314323
315324 if (nextCookieFamily != null ) {
@@ -321,11 +330,18 @@ public List<Cookie> splitUidsIntoCookies(UidsCookie uidsCookie) {
321330 return splitCookies ;
322331 }
323332
333+ private static int calculateCookieSize (int uidsIndex , String cookieFamily , String uid ) {
334+ final int approximateBase64CookieFamilySize = (int ) Math .ceil (cookieFamily .length () * 1.33 );
335+ final int approximateBase64UidSize = (int ) Math .ceil (uid .length () * 1.33 );
336+ final int uidsIndexSize = uidsIndex == 0 ? 0 : 2 ;
337+
338+ return uidsIndexSize + approximateBase64CookieFamilySize + approximateBase64UidSize ;
339+ }
340+
324341 private Iterator <String > cookieFamilyNamesByDescPriorityAndExpiration (UidsCookie uidsCookie ) {
325342 return uidsCookie .getCookieUids ().getUids ().entrySet ().stream ()
326343 .sorted (this ::compareCookieFamilyNames )
327344 .map (Map .Entry ::getKey )
328- .toList ()
329345 .iterator ();
330346 }
331347
@@ -352,14 +368,6 @@ private void updateSyncSizeMetrics(String nextCookieFamily) {
352368 }
353369 }
354370
355- private boolean cookieExceededMaxLength (String name , UidsCookie uidsCookie ) {
356- return maxCookieSizeBytes > 0 && cookieBytesLength (name , uidsCookie ) > maxCookieSizeBytes ;
357- }
358-
359- private int cookieBytesLength (String cookieName , UidsCookie uidsCookie ) {
360- return aliveCookie (cookieName , uidsCookie ).encode ().getBytes ().length ;
361- }
362-
363371 public String hostCookieUidToSync (RoutingContext routingContext , String cookieFamilyName ) {
364372 if (!StringUtils .equals (cookieFamilyName , hostCookieFamily )) {
365373 return null ;
0 commit comments