@@ -20,6 +20,11 @@ class Site_Details_Index {
20
20
*/
21
21
private $ timestamp = null ;
22
22
23
+ const SYNC_DATA_OPTION = 'vip_config_sync_data ' ;
24
+
25
+ const MINUTE_IN_MS = MINUTE_IN_SECONDS * 1000 ;
26
+ const DAY_IN_MS = DAY_IN_SECONDS * 1000 ;
27
+
23
28
/**
24
29
* Standard singleton except accept a timestamp for mocking purposes.
25
30
*
@@ -349,32 +354,127 @@ public function get_site_details() {
349
354
}
350
355
351
356
/**
352
- * Builds the site details structure and then puts it into logstash
353
- * and sends it to the site details service
357
+ * Builds the site details and sends it to the site details service.
354
358
*/
355
359
public function put_site_details () {
356
360
$ site_details = $ this ->get_site_details ();
357
- $ url = null ;
358
- $ token = null ;
361
+ $ sync_data = get_option ( self ::SYNC_DATA_OPTION , [] );
362
+ $ sync_data = is_array ( $ sync_data ) ? $ sync_data : [];
363
+
364
+ $ sync_type = $ this ->determine_sync_type ( $ site_details , $ sync_data );
365
+
366
+ // Run a heartbeat sync.
367
+ if ( 'heartbeat ' === $ sync_type ) {
368
+ $ success = $ this ->send_sync ( '/sites/heartbeat ' , [
369
+ 'client_site_id ' => $ site_details ['client_site_id ' ],
370
+ 'blog_id ' => $ site_details ['core ' ]['blog_id ' ],
371
+ 'timestamp ' => $ site_details ['timestamp ' ],
372
+ ] );
373
+
374
+ if ( $ success ) {
375
+ $ sync_data ['last_synced ' ] = $ site_details ['timestamp ' ];
376
+ update_option ( self ::SYNC_DATA_OPTION , $ sync_data , false );
377
+ }
378
+ }
379
+
380
+ // Run a full sync.
381
+ if ( 'full ' === $ sync_type ) {
382
+ $ success = $ this ->send_sync ( '/sites ' , $ site_details );
359
383
360
- if ( defined ( 'VIP_SERVICES_AUTH_TOKENS ' ) && ! empty ( VIP_SERVICES_AUTH_TOKENS ) ) {
361
- $ auth_token_details = json_decode ( base64_decode ( VIP_SERVICES_AUTH_TOKENS ), true );
362
- $ url = $ auth_token_details ['site ' ]['vip-site-details ' ]['url ' ] ?? null ;
363
- $ token = $ auth_token_details ['site ' ]['vip-site-details ' ]['token ' ] ?? null ;
384
+ if ( $ success ) {
385
+ $ timestamp = $ site_details ['timestamp ' ];
386
+
387
+ // Stagger the future syncs if this is the first time we're setting the option.
388
+ if ( empty ( $ sync_data ['last_full_synced ' ] ) ) {
389
+ $ sync_data ['last_full_synced ' ] = wp_rand ( $ timestamp - self ::DAY_IN_MS , $ timestamp );
390
+ $ sync_data ['last_synced ' ] = wp_rand ( $ timestamp - self ::MINUTE_IN_MS * 25 , $ timestamp );
391
+ } else {
392
+ $ sync_data ['last_full_synced ' ] = $ timestamp ;
393
+ $ sync_data ['last_synced ' ] = $ timestamp ;
394
+ }
395
+
396
+ $ sync_data ['last_sync_hash ' ] = $ this ->get_site_details_data_hash ( $ site_details );
397
+ update_option ( self ::SYNC_DATA_OPTION , $ sync_data , false );
398
+ }
364
399
}
400
+ }
365
401
366
- if ( $ url && $ token ) {
367
- $ args = array (
368
- 'method ' => 'PUT ' ,
369
- 'body ' => wp_json_encode ( $ site_details ),
370
- 'headers ' => array (
371
- 'Authorization ' => 'Bearer ' . $ token ,
372
- 'Content-Type ' => 'application/json ' ,
373
- ),
374
- );
402
+ /**
403
+ * Determine if we need a full sync, a heartbeat, or none at all.
404
+ */
405
+ private function determine_sync_type ( $ site_details , $ sync_data ) {
406
+ $ current_timestamp = $ site_details ['timestamp ' ];
407
+ $ last_sync_timestamp = $ sync_data ['last_synced ' ] ?? 0 ;
408
+ $ last_full_sync_timestamp = $ sync_data ['last_full_synced ' ] ?? 0 ;
409
+
410
+ // Safeguard a reset on the timestamps if they have been incorrectly altered.
411
+ $ max_allowed_timestamp = $ current_timestamp + ( self ::MINUTE_IN_MS * 5 );
412
+ if ( $ last_sync_timestamp > $ max_allowed_timestamp || $ last_full_sync_timestamp > $ max_allowed_timestamp ) {
413
+ $ last_full_sync_timestamp = 0 ;
414
+ $ last_sync_timestamp = 0 ;
415
+ }
416
+
417
+ // Send a full sync at least once per day.
418
+ if ( $ current_timestamp - $ last_full_sync_timestamp > self ::DAY_IN_MS ) {
419
+ return 'full ' ;
420
+ }
421
+
422
+ $ current_data_hash = $ this ->get_site_details_data_hash ( $ site_details );
423
+ $ last_data_hash = $ sync_data ['last_sync_hash ' ] ?? '' ;
424
+
425
+ // Send a full sync if the data has changed.
426
+ if ( $ current_data_hash !== $ last_data_hash ) {
427
+ return 'full ' ;
428
+ }
375
429
376
- vip_safe_wp_remote_request ( rtrim ( $ url , '/ ' ) . '/sites ' , false , 3 , 5 , 10 , $ args );
430
+ // Send a heartbeat if it's been more than 25 minutes (1/3 of the stale threshold).
431
+ if ( $ current_timestamp - $ last_sync_timestamp > self ::MINUTE_IN_MS * 25 ) {
432
+ return 'heartbeat ' ;
377
433
}
434
+
435
+ return 'none ' ;
436
+ }
437
+
438
+ /**
439
+ * Hashes the data for comparison purposes, removing the timestamp.
440
+ */
441
+ private function get_site_details_data_hash ( $ site_details ) {
442
+ unset( $ site_details ['timestamp ' ] );
443
+ return hash ( 'sha256 ' , wp_json_encode ( $ site_details ) );
444
+ }
445
+
446
+ /**
447
+ * Sends sync data to the service.
448
+ */
449
+ private function send_sync ( $ endpoint , $ body ) {
450
+ if ( ! defined ( 'VIP_SERVICES_AUTH_TOKENS ' ) || empty ( VIP_SERVICES_AUTH_TOKENS ) ) {
451
+ return false ;
452
+ }
453
+
454
+ $ auth_tokens = json_decode ( base64_decode ( VIP_SERVICES_AUTH_TOKENS ), true );
455
+
456
+ $ url = $ auth_tokens ['site ' ]['vip-site-details ' ]['url ' ] ?? null ;
457
+ $ token = $ auth_tokens ['site ' ]['vip-site-details ' ]['token ' ] ?? null ;
458
+ if ( ! $ url || ! $ token ) {
459
+ return false ;
460
+ }
461
+
462
+ $ response = vip_safe_wp_remote_request ( rtrim ( $ url , '/ ' ) . $ endpoint , false , 3 , 5 , 10 , [
463
+ 'method ' => 'PUT ' ,
464
+ 'body ' => wp_json_encode ( $ body ),
465
+ 'headers ' => array (
466
+ 'Authorization ' => 'Bearer ' . $ token ,
467
+ 'Content-Type ' => 'application/json ' ,
468
+ ),
469
+ ] );
470
+
471
+ $ response_code = (int ) wp_remote_retrieve_response_code ( $ response );
472
+ if ( is_wp_error ( $ response ) || 200 !== $ response_code ) {
473
+ return false ;
474
+ }
475
+
476
+ $ response_body = json_decode ( wp_remote_retrieve_body ( $ response ), true );
477
+ return isset ( $ response_body ['updated ' ] ) && true === $ response_body ['updated ' ];
378
478
}
379
479
380
480
/**
0 commit comments