1pub use http::Extensions;
18use log::warn;
19use once_cell::sync::{Lazy, OnceCell};
20use pingora_error::{Error, ErrorType::*, OrErr, Result};
21use pingora_header_serde::HeaderSerde;
22use pingora_http::{HMap, ResponseHeader};
23use serde::{Deserialize, Serialize};
24use std::borrow::Cow;
25use std::time::{Duration, SystemTime};
26
27use crate::key::HashBinary;
28
29pub(crate) type InternalMeta = internal_meta::InternalMetaLatest;
30mod internal_meta {
31 use super::*;
32
33 pub(crate) type InternalMetaLatest = InternalMetaV2;
34
35 #[derive(Debug, Deserialize, Serialize, Clone)]
36 pub(crate) struct InternalMetaV0 {
37 pub(crate) fresh_until: SystemTime,
38 pub(crate) created: SystemTime,
39 pub(crate) stale_while_revalidate_sec: u32,
40 pub(crate) stale_if_error_sec: u32,
41 }
43
44 impl InternalMetaV0 {
45 #[allow(dead_code)]
46 fn serialize(&self) -> Result<Vec<u8>> {
47 rmp_serde::encode::to_vec(self).or_err(InternalError, "failed to encode cache meta")
48 }
49
50 fn deserialize(buf: &[u8]) -> Result<Self> {
51 rmp_serde::decode::from_slice(buf)
52 .or_err(InternalError, "failed to decode cache meta v0")
53 }
54 }
55
56 #[derive(Debug, Deserialize, Serialize, Clone)]
57 pub(crate) struct InternalMetaV1 {
58 pub(crate) version: u8,
59 pub(crate) fresh_until: SystemTime,
60 pub(crate) created: SystemTime,
61 pub(crate) stale_while_revalidate_sec: u32,
62 pub(crate) stale_if_error_sec: u32,
63 }
65
66 impl InternalMetaV1 {
67 #[allow(dead_code)]
68 pub const VERSION: u8 = 1;
69
70 #[allow(dead_code)]
71 pub fn serialize(&self) -> Result<Vec<u8>> {
72 assert_eq!(self.version, 1);
73 rmp_serde::encode::to_vec(self).or_err(InternalError, "failed to encode cache meta")
74 }
75
76 fn deserialize(buf: &[u8]) -> Result<Self> {
77 rmp_serde::decode::from_slice(buf)
78 .or_err(InternalError, "failed to decode cache meta v1")
79 }
80 }
81
82 #[derive(Debug, Deserialize, Serialize, Clone)]
83 pub(crate) struct InternalMetaV2 {
84 pub(crate) version: u8,
85 pub(crate) fresh_until: SystemTime,
86 pub(crate) created: SystemTime,
87 pub(crate) updated: SystemTime,
88 pub(crate) stale_while_revalidate_sec: u32,
89 pub(crate) stale_if_error_sec: u32,
90 #[serde(default)]
96 pub(crate) variance: Option<HashBinary>,
97 #[serde(default)]
98 #[serde(skip_serializing_if = "Option::is_none")]
99 pub(crate) epoch_override: Option<SystemTime>,
100 }
101
102 impl Default for InternalMetaV2 {
103 fn default() -> Self {
104 let epoch = SystemTime::UNIX_EPOCH;
105 InternalMetaV2 {
106 version: InternalMetaV2::VERSION,
107 fresh_until: epoch,
108 created: epoch,
109 updated: epoch,
110 stale_while_revalidate_sec: 0,
111 stale_if_error_sec: 0,
112 variance: None,
113 epoch_override: None,
114 }
115 }
116 }
117
118 impl InternalMetaV2 {
119 pub const VERSION: u8 = 2;
120
121 pub fn serialize(&self) -> Result<Vec<u8>> {
122 assert_eq!(self.version, Self::VERSION);
123 rmp_serde::encode::to_vec(self).or_err(InternalError, "failed to encode cache meta")
124 }
125
126 fn deserialize(buf: &[u8]) -> Result<Self> {
127 rmp_serde::decode::from_slice(buf)
128 .or_err(InternalError, "failed to decode cache meta v2")
129 }
130 }
131
132 impl From<InternalMetaV0> for InternalMetaV2 {
133 fn from(v0: InternalMetaV0) -> Self {
134 InternalMetaV2 {
135 version: InternalMetaV2::VERSION,
136 fresh_until: v0.fresh_until,
137 created: v0.created,
138 updated: v0.created,
139 stale_while_revalidate_sec: v0.stale_while_revalidate_sec,
140 stale_if_error_sec: v0.stale_if_error_sec,
141 ..Default::default()
142 }
143 }
144 }
145
146 impl From<InternalMetaV1> for InternalMetaV2 {
147 fn from(v1: InternalMetaV1) -> Self {
148 InternalMetaV2 {
149 version: InternalMetaV2::VERSION,
150 fresh_until: v1.fresh_until,
151 created: v1.created,
152 updated: v1.created,
153 stale_while_revalidate_sec: v1.stale_while_revalidate_sec,
154 stale_if_error_sec: v1.stale_if_error_sec,
155 ..Default::default()
156 }
157 }
158 }
159
160 pub(crate) fn deserialize(buf: &[u8]) -> Result<InternalMetaLatest> {
162 const MIN_SIZE: usize = 10; if buf.len() < MIN_SIZE {
164 return Error::e_explain(
165 InternalError,
166 format!("Buf too short ({}) to be InternalMeta", buf.len()),
167 );
168 }
169 let preread_buf = &mut &buf[..MIN_SIZE];
170 match rmp::decode::read_array_len(preread_buf)
172 .or_err(InternalError, "failed to decode cache meta array size")?
173 {
174 4 => Ok(InternalMetaV0::deserialize(buf)?.into()),
176 _ => {
178 let version = rmp::decode::read_pfix(preread_buf)
181 .or_err(InternalError, "failed to decode meta version")?;
182 match version {
183 1 => Ok(InternalMetaV1::deserialize(buf)?.into()),
184 2 => InternalMetaV2::deserialize(buf),
185 _ => Error::e_explain(
186 InternalError,
187 format!("Unknown InternalMeta version {version}"),
188 ),
189 }
190 }
191 }
192 }
193
194 #[cfg(test)]
195 mod tests {
196 use super::*;
197
198 #[test]
199 fn test_internal_meta_serde_v0() {
200 let meta = InternalMetaV0 {
201 fresh_until: SystemTime::now(),
202 created: SystemTime::now(),
203 stale_while_revalidate_sec: 0,
204 stale_if_error_sec: 0,
205 };
206 let binary = meta.serialize().unwrap();
207 let meta2 = InternalMetaV0::deserialize(&binary).unwrap();
208 assert_eq!(meta.fresh_until, meta2.fresh_until);
209 }
210
211 #[test]
212 fn test_internal_meta_serde_v1() {
213 let meta = InternalMetaV1 {
214 version: InternalMetaV1::VERSION,
215 fresh_until: SystemTime::now(),
216 created: SystemTime::now(),
217 stale_while_revalidate_sec: 0,
218 stale_if_error_sec: 0,
219 };
220 let binary = meta.serialize().unwrap();
221 let meta2 = InternalMetaV1::deserialize(&binary).unwrap();
222 assert_eq!(meta.fresh_until, meta2.fresh_until);
223 }
224
225 #[test]
226 fn test_internal_meta_serde_v2() {
227 let meta = InternalMetaV2::default();
228 let binary = meta.serialize().unwrap();
229 let meta2 = InternalMetaV2::deserialize(&binary).unwrap();
230 assert_eq!(meta2.version, 2);
231 assert_eq!(meta.fresh_until, meta2.fresh_until);
232 assert_eq!(meta.created, meta2.created);
233 assert_eq!(meta.updated, meta2.updated);
234 }
235
236 #[test]
237 fn test_internal_meta_serde_across_versions() {
238 let meta = InternalMetaV0 {
239 fresh_until: SystemTime::now(),
240 created: SystemTime::now(),
241 stale_while_revalidate_sec: 0,
242 stale_if_error_sec: 0,
243 };
244 let binary = meta.serialize().unwrap();
245 let meta2 = deserialize(&binary).unwrap();
246 assert_eq!(meta2.version, 2);
247 assert_eq!(meta.fresh_until, meta2.fresh_until);
248
249 let meta = InternalMetaV1 {
250 version: 1,
251 fresh_until: SystemTime::now(),
252 created: SystemTime::now(),
253 stale_while_revalidate_sec: 0,
254 stale_if_error_sec: 0,
255 };
256 let binary = meta.serialize().unwrap();
257 let meta2 = deserialize(&binary).unwrap();
258 assert_eq!(meta2.version, 2);
259 assert_eq!(meta.fresh_until, meta2.fresh_until);
260 assert_eq!(meta2.created, meta2.updated);
262 }
263
264 #[derive(Deserialize, Serialize)]
267 struct InternalMetaV2Base {
268 version: u8,
269 fresh_until: SystemTime,
270 created: SystemTime,
271 updated: SystemTime,
272 stale_while_revalidate_sec: u32,
273 stale_if_error_sec: u32,
274 }
275
276 impl InternalMetaV2Base {
277 pub const VERSION: u8 = 2;
278 pub fn serialize(&self) -> Result<Vec<u8>> {
279 assert!(self.version >= Self::VERSION);
280 rmp_serde::encode::to_vec(self).or_err(InternalError, "failed to encode cache meta")
281 }
282 fn deserialize(buf: &[u8]) -> Result<Self> {
283 rmp_serde::decode::from_slice(buf)
284 .or_err(InternalError, "failed to decode cache meta v2")
285 }
286 }
287
288 #[derive(Deserialize, Serialize)]
290 struct InternalMetaV2BaseWithVariance {
291 version: u8,
292 fresh_until: SystemTime,
293 created: SystemTime,
294 updated: SystemTime,
295 stale_while_revalidate_sec: u32,
296 stale_if_error_sec: u32,
297 #[serde(default)]
298 #[serde(skip_serializing_if = "Option::is_none")]
299 variance: Option<HashBinary>,
300 }
301
302 impl Default for InternalMetaV2BaseWithVariance {
303 fn default() -> Self {
304 let epoch = SystemTime::UNIX_EPOCH;
305 InternalMetaV2BaseWithVariance {
306 version: InternalMetaV2::VERSION,
307 fresh_until: epoch,
308 created: epoch,
309 updated: epoch,
310 stale_while_revalidate_sec: 0,
311 stale_if_error_sec: 0,
312 variance: None,
313 }
314 }
315 }
316
317 impl InternalMetaV2BaseWithVariance {
318 pub const VERSION: u8 = 2;
319 pub fn serialize(&self) -> Result<Vec<u8>> {
320 assert!(self.version >= Self::VERSION);
321 rmp_serde::encode::to_vec(self).or_err(InternalError, "failed to encode cache meta")
322 }
323 fn deserialize(buf: &[u8]) -> Result<Self> {
324 rmp_serde::decode::from_slice(buf)
325 .or_err(InternalError, "failed to decode cache meta v2")
326 }
327 }
328
329 #[test]
330 fn test_internal_meta_serde_v2_extend_fields_variance() {
331 let meta = InternalMetaV2BaseWithVariance::default();
333 let binary = meta.serialize().unwrap();
334 let meta2 = InternalMetaV2Base::deserialize(&binary).unwrap();
335 assert_eq!(meta2.version, 2);
336 assert_eq!(meta.fresh_until, meta2.fresh_until);
337 assert_eq!(meta.created, meta2.created);
338 assert_eq!(meta.updated, meta2.updated);
339
340 let now = SystemTime::now();
342 let meta = InternalMetaV2Base {
343 version: InternalMetaV2::VERSION,
344 fresh_until: now,
345 created: now,
346 updated: now,
347 stale_while_revalidate_sec: 0,
348 stale_if_error_sec: 0,
349 };
350 let binary = meta.serialize().unwrap();
351 let meta2 = InternalMetaV2BaseWithVariance::deserialize(&binary).unwrap();
352 assert_eq!(meta2.version, 2);
353 assert_eq!(meta.fresh_until, meta2.fresh_until);
354 assert_eq!(meta.created, meta2.created);
355 assert_eq!(meta.updated, meta2.updated);
356 }
357
358 #[test]
359 fn test_internal_meta_serde_v2_extend_fields_epoch_override() {
360 let now = SystemTime::now();
361
362 let meta = InternalMetaV2 {
364 fresh_until: now,
365 created: now,
366 updated: now,
367 epoch_override: None, ..Default::default()
369 };
370 let binary = meta.serialize().unwrap();
371 let meta2 = InternalMetaV2BaseWithVariance::deserialize(&binary).unwrap();
372 assert_eq!(meta2.version, 2);
373 assert_eq!(meta.fresh_until, meta2.fresh_until);
374 assert_eq!(meta.created, meta2.created);
375 assert_eq!(meta.updated, meta2.updated);
376 assert!(meta2.variance.is_none());
377
378 let mut meta = InternalMetaV2BaseWithVariance {
380 version: InternalMetaV2::VERSION,
381 fresh_until: now,
382 created: now,
383 updated: now,
384 stale_while_revalidate_sec: 0,
385 stale_if_error_sec: 0,
386 variance: None,
387 };
388 let binary = meta.serialize().unwrap();
389 let meta2 = InternalMetaV2::deserialize(&binary).unwrap();
390 assert_eq!(meta2.version, 2);
391 assert_eq!(meta.fresh_until, meta2.fresh_until);
392 assert_eq!(meta.created, meta2.created);
393 assert_eq!(meta.updated, meta2.updated);
394 assert!(meta2.variance.is_none());
395 assert!(meta2.epoch_override.is_none());
396
397 meta.variance = Some(*b"variance_testing");
399 let binary = meta.serialize().unwrap();
400 let meta2 = InternalMetaV2::deserialize(&binary).unwrap();
401 assert_eq!(meta2.version, 2);
402 assert_eq!(meta.fresh_until, meta2.fresh_until);
403 assert_eq!(meta.created, meta2.created);
404 assert_eq!(meta.updated, meta2.updated);
405 assert_eq!(meta.variance, meta2.variance);
406 assert!(meta2.epoch_override.is_none());
407 }
408 }
409}
410
411#[derive(Debug)]
412pub(crate) struct CacheMetaInner {
413 pub(crate) internal: InternalMeta,
415 pub(crate) header: ResponseHeader,
416 pub extensions: Extensions,
419}
420
421#[derive(Debug)]
423pub struct CacheMeta(pub(crate) Box<CacheMetaInner>);
424
425impl CacheMeta {
426 pub fn new(
428 fresh_until: SystemTime,
429 created: SystemTime,
430 stale_while_revalidate_sec: u32,
431 stale_if_error_sec: u32,
432 header: ResponseHeader,
433 ) -> CacheMeta {
434 CacheMeta(Box::new(CacheMetaInner {
435 internal: InternalMeta {
436 version: InternalMeta::VERSION,
437 fresh_until,
438 created,
439 updated: created, stale_while_revalidate_sec,
441 stale_if_error_sec,
442 ..Default::default()
443 },
444 header,
445 extensions: Extensions::new(),
446 }))
447 }
448
449 pub fn created(&self) -> SystemTime {
451 self.0.internal.created
452 }
453
454 pub fn updated(&self) -> SystemTime {
458 self.0.internal.updated
459 }
460
461 pub fn epoch(&self) -> SystemTime {
466 self.0.internal.epoch_override.unwrap_or(self.updated())
467 }
468
469 pub fn epoch_override(&self) -> Option<SystemTime> {
471 self.0.internal.epoch_override
472 }
473
474 pub fn set_epoch_override(&mut self, epoch: SystemTime) {
479 self.0.internal.epoch_override = Some(epoch);
480 }
481
482 pub fn remove_epoch_override(&mut self) {
484 self.0.internal.epoch_override = None;
485 }
486
487 pub fn is_fresh(&self, time: SystemTime) -> bool {
489 self.0.internal.fresh_until >= time
491 }
492
493 pub fn fresh_sec(&self) -> u64 {
498 let reference = self.epoch();
502 self.0
503 .internal
504 .fresh_until
505 .duration_since(reference)
506 .map_or(0, |duration| duration.as_secs())
507 }
508
509 pub fn fresh_until(&self) -> SystemTime {
511 self.0.internal.fresh_until
512 }
513
514 pub fn age(&self) -> Duration {
518 let reference = self.epoch();
519 SystemTime::now()
520 .duration_since(reference)
521 .unwrap_or_default()
522 }
523
524 pub fn stale_while_revalidate_sec(&self) -> u32 {
526 self.0.internal.stale_while_revalidate_sec
527 }
528
529 pub fn stale_if_error_sec(&self) -> u32 {
531 self.0.internal.stale_if_error_sec
532 }
533
534 pub fn serve_stale_while_revalidate(&self, time: SystemTime) -> bool {
539 self.can_serve_stale(self.0.internal.stale_while_revalidate_sec, time)
540 }
541
542 pub fn serve_stale_if_error(&self, time: SystemTime) -> bool {
547 self.can_serve_stale(self.0.internal.stale_if_error_sec, time)
548 }
549
550 pub fn disable_serve_stale(&mut self) {
552 self.0.internal.stale_if_error_sec = 0;
553 self.0.internal.stale_while_revalidate_sec = 0;
554 }
555
556 pub fn variance(&self) -> Option<HashBinary> {
558 self.0.internal.variance
559 }
560
561 pub fn set_variance_key(&mut self, variance_key: HashBinary) {
563 self.0.internal.variance = Some(variance_key);
564 }
565
566 pub fn set_variance(&mut self, variance: HashBinary) {
568 self.0.internal.variance = Some(variance)
569 }
570
571 pub fn remove_variance(&mut self) {
573 self.0.internal.variance = None
574 }
575
576 pub fn response_header(&self) -> &ResponseHeader {
578 &self.0.header
579 }
580
581 pub fn response_header_mut(&mut self) -> &mut ResponseHeader {
583 &mut self.0.header
584 }
585
586 pub fn extensions(&self) -> &Extensions {
588 &self.0.extensions
589 }
590
591 pub fn extensions_mut(&mut self) -> &mut Extensions {
593 &mut self.0.extensions
594 }
595
596 pub fn response_header_copy(&self) -> ResponseHeader {
598 self.0.header.clone()
599 }
600
601 pub fn headers(&self) -> &HMap {
603 &self.0.header.headers
604 }
605
606 fn can_serve_stale(&self, serve_stale_sec: u32, time: SystemTime) -> bool {
607 if serve_stale_sec == 0 {
608 return false;
609 }
610 if let Some(stale_until) = self
611 .0
612 .internal
613 .fresh_until
614 .checked_add(Duration::from_secs(serve_stale_sec.into()))
615 {
616 stale_until >= time
617 } else {
618 true
620 }
621 }
622
623 pub fn serialize(&self) -> Result<(Vec<u8>, Vec<u8>)> {
625 let internal = self.0.internal.serialize()?;
626 let header = header_serialize(&self.0.header)?;
627 log::debug!("header to serialize: {:?}", &self.0.header);
628 Ok((internal, header))
629 }
630
631 pub fn deserialize(internal: &[u8], header: &[u8]) -> Result<Self> {
633 let internal = internal_meta::deserialize(internal)?;
634 let header = header_deserialize(header)?;
635 Ok(CacheMeta(Box::new(CacheMetaInner {
636 internal,
637 header,
638 extensions: Extensions::new(),
639 })))
640 }
641}
642
643use http::StatusCode;
644
645pub type FreshDurationByStatusFn = fn(StatusCode) -> Option<Duration>;
647
648pub struct CacheMetaDefaults {
650 fresh_sec_fn: FreshDurationByStatusFn,
652 stale_while_revalidate_sec: u32,
653 stale_if_error_sec: u32,
655}
656
657impl CacheMetaDefaults {
658 pub const fn new(
660 fresh_sec_fn: FreshDurationByStatusFn,
661 stale_while_revalidate_sec: u32,
662 stale_if_error_sec: u32,
663 ) -> Self {
664 CacheMetaDefaults {
665 fresh_sec_fn,
666 stale_while_revalidate_sec,
667 stale_if_error_sec,
668 }
669 }
670
671 pub fn fresh_sec(&self, resp_status: StatusCode) -> Option<Duration> {
675 if resp_status == StatusCode::NOT_MODIFIED {
677 (self.fresh_sec_fn)(StatusCode::OK)
678 } else {
679 (self.fresh_sec_fn)(resp_status)
680 }
681 }
682
683 pub fn serve_stale_while_revalidate_sec(&self) -> u32 {
685 self.stale_while_revalidate_sec
686 }
687
688 pub fn serve_stale_if_error_sec(&self) -> u32 {
690 self.stale_if_error_sec
691 }
692}
693
694static COMPRESSION_DICT_CONTENT: OnceCell<Cow<'static, [u8]>> = OnceCell::new();
698
699static HEADER_SERDE: Lazy<HeaderSerde> = Lazy::new(|| {
700 let dict_opt = if let Some(dict_content) = COMPRESSION_DICT_CONTENT.get() {
701 Some(dict_content.to_vec())
702 } else {
703 warn!("no header compression dictionary loaded - use set_compression_dict_content() or set_compression_dict_path() to set one");
704 None
705 };
706
707 HeaderSerde::new(dict_opt)
708});
709
710pub(crate) fn header_serialize(header: &ResponseHeader) -> Result<Vec<u8>> {
711 HEADER_SERDE.serialize(header)
712}
713
714pub(crate) fn header_deserialize<T: AsRef<[u8]>>(buf: T) -> Result<ResponseHeader> {
715 HEADER_SERDE.deserialize(buf.as_ref())
716}
717
718pub fn set_compression_dict_path(path: &str) -> bool {
724 match std::fs::read(path) {
725 Ok(dict) => COMPRESSION_DICT_CONTENT.set(dict.into()).is_ok(),
726 Err(e) => {
727 warn!(
728 "failed to read header compress dictionary file at {}, {:?}",
729 path, e
730 );
731 false
732 }
733 }
734}
735
736pub fn set_compression_dict_content(content: Cow<'static, [u8]>) -> bool {
743 COMPRESSION_DICT_CONTENT.set(content).is_ok()
744}
745
746#[cfg(test)]
747mod tests {
748 use super::*;
749 use std::time::Duration;
750
751 #[test]
752 fn test_cache_meta_age_without_override() {
753 let now = SystemTime::now();
754 let header = ResponseHeader::build_no_case(200, None).unwrap();
755 let meta = CacheMeta::new(now + Duration::from_secs(300), now, 0, 0, header);
756
757 std::thread::sleep(Duration::from_millis(100));
759 let age = meta.age();
760 assert!(age.as_secs() < 1, "age should be close to 0");
761
762 assert_eq!(meta.epoch(), meta.updated());
764 }
765
766 #[test]
767 fn test_cache_meta_age_with_epoch_override_past() {
768 let now = SystemTime::now();
769 let header = ResponseHeader::build(200, None).unwrap();
770 let mut meta = CacheMeta::new(now + Duration::from_secs(300), now, 0, 0, header);
771
772 let epoch_override = now - Duration::from_secs(10);
774 meta.set_epoch_override(epoch_override);
775
776 let age = meta.age();
778 assert!(age.as_secs() >= 10);
779 assert!(age.as_secs() < 12);
780
781 assert_eq!(meta.epoch(), epoch_override);
783 assert_eq!(meta.epoch_override(), Some(epoch_override));
784 }
785
786 #[test]
787 fn test_cache_meta_age_with_epoch_override_future() {
788 let now = SystemTime::now();
789 let header = ResponseHeader::build(200, None).unwrap();
790 let mut meta = CacheMeta::new(now + Duration::from_secs(100), now, 0, 0, header);
791
792 let future_epoch = now + Duration::from_secs(10);
794 meta.set_epoch_override(future_epoch);
795
796 let age_with_epoch = meta.age();
797 assert_eq!(age_with_epoch, Duration::ZERO);
799 }
800
801 #[test]
802 fn test_cache_meta_fresh_sec() {
803 let header = ResponseHeader::build(StatusCode::OK, None).unwrap();
804 let mut meta = CacheMeta::new(
805 SystemTime::now() + Duration::from_secs(100),
806 SystemTime::now() - Duration::from_secs(100),
807 0,
808 0,
809 header,
810 );
811
812 meta.0.internal.updated = SystemTime::UNIX_EPOCH + Duration::from_secs(1000);
813 meta.0.internal.fresh_until = SystemTime::UNIX_EPOCH + Duration::from_secs(1100);
814
815 let fresh_sec_without_override = meta.fresh_sec();
817 assert_eq!(fresh_sec_without_override, 100); let epoch_override = SystemTime::UNIX_EPOCH + Duration::from_secs(1050);
821 meta.set_epoch_override(epoch_override);
822 assert_eq!(meta.epoch_override(), Some(epoch_override));
823 assert_eq!(meta.epoch(), epoch_override);
824
825 let fresh_sec_with_override = meta.fresh_sec();
826 assert_eq!(fresh_sec_with_override, 50);
828
829 meta.remove_epoch_override();
830 assert_eq!(meta.epoch_override(), None);
831 assert_eq!(meta.epoch(), meta.updated());
832 assert_eq!(meta.fresh_sec(), 100); }
834}