Skip to content

Commit a3b0d7c

Browse files
authored
Merge pull request #1940 from ShyamGadde/add/option-to-disable-freshness-ttl
Allow disabling timestamp-based freshness checks by using negative TTL values
2 parents 69b7a14 + b5e1892 commit a3b0d7c

File tree

8 files changed

+97
-97
lines changed

8 files changed

+97
-97
lines changed

plugins/optimization-detective/class-od-url-metric-group-collection.php

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ final class OD_URL_Metric_Group_Collection implements Countable, IteratorAggrega
7373
* A freshness age of zero means a URL Metric will always be considered stale.
7474
*
7575
* @since 0.1.0
76-
* @var int<0, max>
76+
* @var int<-1, max>
7777
*/
7878
private $freshness_ttl;
7979

@@ -104,7 +104,7 @@ final class OD_URL_Metric_Group_Collection implements Countable, IteratorAggrega
104104
*
105105
* @phpstan-param positive-int[] $breakpoints
106106
* @phpstan-param int<1, max> $sample_size
107-
* @phpstan-param int<0, max> $freshness_ttl
107+
* @phpstan-param int<-1, max> $freshness_ttl
108108
*
109109
* @param OD_URL_Metric[] $url_metrics URL Metrics.
110110
* @param non-empty-string $current_etag The current ETag.
@@ -168,18 +168,7 @@ public function __construct( array $url_metrics, string $current_etag, array $br
168168
$this->sample_size = $sample_size;
169169

170170
// Set freshness TTL.
171-
if ( $freshness_ttl < 0 ) {
172-
throw new InvalidArgumentException(
173-
esc_html(
174-
sprintf(
175-
/* translators: %d is the invalid sample size */
176-
__( 'Freshness TTL must be at least zero, but provided: %d', 'optimization-detective' ),
177-
$freshness_ttl
178-
)
179-
)
180-
);
181-
}
182-
$this->freshness_ttl = $freshness_ttl;
171+
$this->freshness_ttl = max( -1, $freshness_ttl );
183172

184173
// Create groups and the URL Metrics to them.
185174
$this->groups = $this->create_groups();
@@ -226,7 +215,7 @@ public function get_sample_size(): int {
226215
*
227216
* @since 1.0.0
228217
*
229-
* @return int<0, max> Freshness age (TTL) for a given URL Metric.
218+
* @return int<-1, max> Freshness age (TTL) for a given URL Metric.
230219
*/
231220
public function get_freshness_ttl(): int {
232221
return $this->freshness_ttl;
@@ -702,7 +691,7 @@ public function count(): int {
702691
* @return array{
703692
* current_etag: non-empty-string,
704693
* breakpoints: positive-int[],
705-
* freshness_ttl: 0|positive-int,
694+
* freshness_ttl: int<-1, max>,
706695
* sample_size: positive-int,
707696
* all_element_max_intersection_ratios: array<string, float>,
708697
* common_lcp_element: ?OD_Element,

plugins/optimization-detective/class-od-url-metric-group.php

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ final class OD_URL_Metric_Group implements IteratorAggregate, Countable, JsonSer
6262
*
6363
* @since 0.1.0
6464
*
65-
* @var int<0, max>
65+
* @var int<-1, max>
6666
*/
6767
private $freshness_ttl;
6868

@@ -102,7 +102,7 @@ final class OD_URL_Metric_Group implements IteratorAggregate, Countable, JsonSer
102102
* @phpstan-param int<0, max> $minimum_viewport_width
103103
* @phpstan-param int<1, max>|null $maximum_viewport_width
104104
* @phpstan-param int<1, max> $sample_size
105-
* @phpstan-param int<0, max> $freshness_ttl
105+
* @phpstan-param int<-1, max> $freshness_ttl
106106
*
107107
* @param OD_URL_Metric[] $url_metrics URL Metrics to add to the group.
108108
* @param int $minimum_viewport_width Minimum possible viewport width (exclusive) for the group. Must be zero or greater.
@@ -145,18 +145,7 @@ public function __construct( array $url_metrics, int $minimum_viewport_width, ?i
145145
}
146146
$this->sample_size = $sample_size;
147147

148-
if ( $freshness_ttl < 0 ) {
149-
throw new InvalidArgumentException(
150-
esc_html(
151-
sprintf(
152-
/* translators: %d is the invalid sample size */
153-
__( 'Freshness TTL must be at least zero, but provided: %d', 'optimization-detective' ),
154-
$freshness_ttl
155-
)
156-
)
157-
);
158-
}
159-
$this->freshness_ttl = $freshness_ttl;
148+
$this->freshness_ttl = max( -1, $freshness_ttl );
160149
$this->collection = $collection;
161150
$this->url_metrics = $url_metrics;
162151
}
@@ -203,7 +192,7 @@ public function get_sample_size(): int {
203192
* @since 0.9.0
204193
*
205194
* @todo Eliminate in favor of readonly public property.
206-
* @return int<0, max> Freshness age.
195+
* @return int<-1, max> Freshness age.
207196
*/
208197
public function get_freshness_ttl(): int {
209198
return $this->freshness_ttl;
@@ -285,6 +274,7 @@ static function ( OD_URL_Metric $a, OD_URL_Metric $b ): int {
285274
*
286275
* @since 0.1.0
287276
* @since 0.9.0 If the current environment's generated ETag does not match the URL Metric's ETag, the URL Metric is considered stale.
277+
* @since n.e.x.t Negative freshness TTL values now disable timestamp-based freshness checks.
288278
*
289279
* @return bool Whether complete.
290280
*/
@@ -299,8 +289,8 @@ public function is_complete(): bool {
299289
}
300290
$current_time = microtime( true );
301291
foreach ( $this->url_metrics as $url_metric ) {
302-
// The URL Metric is too old to be fresh.
303-
if ( $current_time > $url_metric->get_timestamp() + $this->freshness_ttl ) {
292+
// The URL Metric is too old to be fresh (skip if freshness TTL is negative).
293+
if ( $this->freshness_ttl >= 0 && $current_time > $url_metric->get_timestamp() + $this->freshness_ttl ) {
304294
return false;
305295
}
306296

@@ -514,7 +504,7 @@ public function clear_cache(): void {
514504
* @since 0.3.1
515505
*
516506
* @return array{
517-
* freshness_ttl: 0|positive-int,
507+
* freshness_ttl: int<-1, max>,
518508
* sample_size: positive-int,
519509
* minimum_viewport_width: int<0, max>,
520510
* maximum_viewport_width: int<1, max>|null,

plugins/optimization-detective/detect.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,8 @@ export default async function detect( {
434434
);
435435
if (
436436
! isNaN( previousVisitTime ) &&
437-
( getCurrentTime() - previousVisitTime ) / 1000 < freshnessTTL
437+
( freshnessTTL < 0 ||
438+
( getCurrentTime() - previousVisitTime ) / 1000 < freshnessTTL )
438439
) {
439440
log(
440441
'The current client session already submitted a fresh URL Metric for this URL so a new one will not be collected now.'

plugins/optimization-detective/docs/hooks.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -238,22 +238,28 @@ add_filter( 'od_metrics_storage_lock_ttl', function ( int $ttl ): int {
238238

239239
### Filter: `od_url_metric_freshness_ttl` (default: 1 week in seconds)
240240

241-
Filters the freshness age (TTL) for a given URL Metric. The freshness TTL must be at least zero, in which it considers URL Metrics to always be stale. In practice, the value should be at least an hour. If your site content does not change frequently, you may want to increase the TTL even longer, say to a month:
241+
Filters age (TTL) for which a URL Metric can be considered fresh.
242+
243+
The freshness TTL (time to live) value can be one of the following values:
244+
245+
* A positive integer (e.g. `3600`, `HOUR_IN_SECONDS`) allows a URL Metric to be fresh for a given period of time into the future.
246+
* A negative integer (`-1`) disables timestamp-based freshness checks, making URL Metrics stay fresh indefinitely unless the current ETag changes.
247+
* A value of zero (`0`) considers URL Metrics to always be stale, which is useful during development. _Never do this on a production site since this can cause a database write for every visitor!_
248+
249+
The default value is `WEEK_IN_SECONDS` since changes to the post/page (or the site overall) will cause a change to the current ETag used for URL Metrics. This causes the relevant existing URL Metrics with the previous ETag to be considered stale, allowing new URL Metrics to be collected before the freshness TTL has expired. See the `od_current_url_metrics_etag_data` filter to customize the ETag data.
250+
251+
For sites where content doesn't change frequently, you can disable the timestamp-based staleness check as follows:
242252

243253
```php
244254
add_filter( 'od_url_metric_freshness_ttl', static function (): int {
245-
return MONTH_IN_SECONDS;
255+
return -1;
246256
} );
247257
```
248258

249-
Note that even if you have large freshness TTL a URL Metric can still become stale sooner; if the page state changes then this results in a change to the ETag associated with a URL Metric. This will allow new URL Metrics to be collected before the freshness TTL has transpired. See the `od_current_url_metrics_etag_data` filter to customize the ETag data.
250-
251-
During development, this can be useful to set to zero so that you don't have to wait for new URL Metrics to be requested when engineering a new optimization:
259+
As noted above, during development you can set the freshness TTL to zero so that you don't have to wait for new URL Metrics to be requested when developing a new optimization:
252260

253261
```php
254-
add_filter( 'od_url_metric_freshness_ttl', static function (): int {
255-
return 0;
256-
} );
262+
add_filter( 'od_url_metric_freshness_ttl', '__return_zero' );
257263
```
258264

259265
### Filter: `od_minimum_viewport_aspect_ratio` (default: 0.4)

plugins/optimization-detective/storage/data.php

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,37 +20,20 @@
2020
* @since 0.1.0
2121
* @access private
2222
*
23-
* @return int<0, max> Expiration TTL in seconds.
23+
* @return int<-1, max> Expiration TTL in seconds.
2424
*/
2525
function od_get_url_metric_freshness_ttl(): int {
2626
/**
27-
* Filters the freshness age (TTL) for a given URL Metric.
28-
*
29-
* The freshness TTL must be at least zero, in which it considers URL Metrics to always be stale.
30-
* In practice, the value should be at least an hour.
27+
* Filters age (TTL) for which a URL Metric can be considered fresh.
3128
*
3229
* @since 0.1.0
30+
* @since n.e.x.t Negative values disable timestamp-based freshness checks.
31+
* @link https://github.com/WordPress/performance/blob/trunk/plugins/optimization-detective/docs/hooks.md#:~:text=Filter%3A%20od_url_metric_freshness_ttl
3332
*
3433
* @param int $ttl Expiration TTL in seconds. Defaults to 1 week.
3534
*/
36-
$freshness_ttl = (int) apply_filters( 'od_url_metric_freshness_ttl', WEEK_IN_SECONDS );
37-
38-
if ( $freshness_ttl < 0 ) {
39-
_doing_it_wrong(
40-
esc_html( "Filter: 'od_url_metric_freshness_ttl'" ),
41-
esc_html(
42-
sprintf(
43-
/* translators: %s is the TTL freshness */
44-
__( 'Freshness TTL must be at least zero, but saw "%s".', 'optimization-detective' ),
45-
$freshness_ttl
46-
)
47-
),
48-
''
49-
);
50-
$freshness_ttl = 0;
51-
}
52-
53-
return $freshness_ttl;
35+
$ttl = (int) apply_filters( 'od_url_metric_freshness_ttl', WEEK_IN_SECONDS );
36+
return max( -1, $ttl );
5437
}
5538

5639
/**
@@ -249,6 +232,7 @@ static function ( $post ): ?array {
249232
* Filters the data that goes into computing the current ETag for URL Metrics.
250233
*
251234
* @since 0.9.0
235+
* @link https://github.com/WordPress/performance/blob/trunk/plugins/optimization-detective/docs/hooks.md#:~:text=Filter%3A%20od_current_url_metrics_etag_data
252236
*
253237
* @param array<string, mixed> $data Data.
254238
*/

plugins/optimization-detective/tests/storage/test-data.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,19 @@ static function (): int {
4646
}
4747

4848
/**
49-
* Test bad od_get_url_metric_freshness_ttl().
49+
* Test negative od_get_url_metric_freshness_ttl().
5050
*
5151
* @covers ::od_get_url_metric_freshness_ttl
5252
*/
53-
public function test_bad_od_get_url_metric_freshness_ttl(): void {
54-
$this->setExpectedIncorrectUsage( 'Filter: &#039;od_url_metric_freshness_ttl&#039;' );
53+
public function test_negative_od_get_url_metric_freshness_ttl(): void {
5554
add_filter(
5655
'od_url_metric_freshness_ttl',
5756
static function (): int {
58-
return -1;
57+
return -12345;
5958
}
6059
);
6160

62-
$this->assertSame( 0, od_get_url_metric_freshness_ttl() );
61+
$this->assertSame( -1, od_get_url_metric_freshness_ttl() );
6362
}
6463

6564
/**

plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,79 +22,79 @@ public function data_provider_test_construction(): array {
2222
$current_etag = md5( '' );
2323

2424
return array(
25-
'no_breakpoints_ok' => array(
25+
'no_breakpoints_ok' => array(
2626
'url_metrics' => array(),
2727
'current_etag' => $current_etag,
2828
'breakpoints' => array(),
2929
'sample_size' => 3,
3030
'freshness_ttl' => HOUR_IN_SECONDS,
3131
'exception' => '',
3232
),
33-
'negative_breakpoint_bad' => array(
33+
'negative_breakpoint_bad' => array(
3434
'url_metrics' => array(),
3535
'current_etag' => $current_etag,
3636
'breakpoints' => array( -1 ),
3737
'sample_size' => 3,
3838
'freshness_ttl' => HOUR_IN_SECONDS,
3939
'exception' => InvalidArgumentException::class,
4040
),
41-
'zero_breakpoint_bad' => array(
41+
'zero_breakpoint_bad' => array(
4242
'url_metrics' => array(),
4343
'current_etag' => $current_etag,
4444
'breakpoints' => array( 0 ),
4545
'sample_size' => 3,
4646
'freshness_ttl' => HOUR_IN_SECONDS,
4747
'exception' => InvalidArgumentException::class,
4848
),
49-
'string_breakpoint_bad' => array(
49+
'string_breakpoint_bad' => array(
5050
'url_metrics' => array(),
5151
'current_etag' => $current_etag,
5252
'breakpoints' => array( 'narrow' ),
5353
'sample_size' => 3,
5454
'freshness_ttl' => HOUR_IN_SECONDS,
5555
'exception' => InvalidArgumentException::class,
5656
),
57-
'negative_sample_size_bad' => array(
57+
'negative_sample_size_bad' => array(
5858
'url_metrics' => array(),
5959
'current_etag' => $current_etag,
6060
'breakpoints' => array( 400 ),
6161
'sample_size' => -3,
6262
'freshness_ttl' => HOUR_IN_SECONDS,
6363
'exception' => InvalidArgumentException::class,
6464
),
65-
'negative_freshness_tll_bad' => array(
65+
'negative_freshness_ttl_ok' => array(
6666
'url_metrics' => array(),
6767
'current_etag' => $current_etag,
6868
'breakpoints' => array( 400 ),
6969
'sample_size' => 3,
7070
'freshness_ttl' => -HOUR_IN_SECONDS,
71-
'exception' => InvalidArgumentException::class,
71+
'exception' => '',
7272
),
73-
'invalid_current_etag_bad' => array(
73+
'invalid_current_etag_bad' => array(
7474
'url_metrics' => array(),
7575
'current_etag' => 'invalid_etag',
7676
'breakpoints' => array( 400 ),
7777
'sample_size' => 3,
7878
'freshness_ttl' => HOUR_IN_SECONDS,
7979
'exception' => InvalidArgumentException::class,
8080
),
81-
'invalid_current_etag_bad2' => array(
81+
'invalid_current_etag_bad2' => array(
8282
'url_metrics' => array(),
8383
'current_etag' => md5( '' ) . PHP_EOL, // Note that /^[a-f0-9]{32}$/ would erroneously validate this. So the \z is required instead in /^[a-f0-9]{32}\z/.
8484
'breakpoints' => array( 400 ),
8585
'sample_size' => 3,
8686
'freshness_ttl' => HOUR_IN_SECONDS,
8787
'exception' => InvalidArgumentException::class,
8888
),
89-
'invalid_url_metrics_bad' => array(
89+
'invalid_url_metrics_bad' => array(
9090
'url_metrics' => array( 'bad' ),
9191
'current_etag' => $current_etag,
9292
'breakpoints' => array( 400 ),
9393
'sample_size' => 3,
9494
'freshness_ttl' => HOUR_IN_SECONDS,
9595
'exception' => TypeError::class,
9696
),
97-
'all_arguments_good' => array(
97+
'all_arguments_good' => array(
9898
'url_metrics' => array(
9999
$this->get_sample_url_metric( array( 'viewport_width' => 200 ) ),
100100
$this->get_sample_url_metric( array( 'viewport_width' => 400 ) ),
@@ -135,7 +135,11 @@ public function test_construction( array $url_metrics, string $current_etag, arr
135135
$this->assertSame( $current_etag, $group_collection->get_current_etag() );
136136
$this->assertSame( $sample_size, $group_collection->get_sample_size() );
137137
$this->assertSame( $breakpoints, $group_collection->get_breakpoints() );
138-
$this->assertSame( $freshness_ttl, $group_collection->get_freshness_ttl() );
138+
if ( $freshness_ttl < 0 ) {
139+
$this->assertSame( -1, $group_collection->get_freshness_ttl() );
140+
} else {
141+
$this->assertSame( $freshness_ttl, $group_collection->get_freshness_ttl() );
142+
}
139143
}
140144

141145
/**

0 commit comments

Comments
 (0)