doc.rst (26452B)
1 HTTP Cache 2 ========== 3 4 This document describes the **HTTP cache implementation**. 5 6 The code resides in `/netwerk/cache2 (searchfox) 7 <https://searchfox.org/mozilla-central/source/netwerk/cache2>`_ 8 9 API 10 --- 11 12 Here is a detailed description of the HTTP cache v2 API, examples 13 included. This document only contains what cannot be found or may not 14 be clear directly from the `IDL files <https://searchfox.org/mozilla-central/search?q=&path=cache2%2FnsICache&case=false®exp=false>`_ comments. 15 16 - The cache API is **completely thread-safe** and **non-blocking**. 17 - There is **no IPC support**. It's only accessible on the default 18 chrome process. 19 - When there is no profile the HTTP cache works, but everything is 20 stored only in memory not obeying any particular limits. 21 22 .. _nsICacheStorageService: 23 24 nsICacheStorageService 25 ---------------------- 26 27 - The HTTP cache entry-point. Accessible as a service only, fully 28 thread-safe, scriptable. 29 30 - `nsICacheStorageService.idl (searchfox) <https://searchfox.org/mozilla-central/source/netwerk/cache2/nsICacheStorageService.idl>`_ 31 32 - \ ``"@mozilla.org/netwerk/cache-storage-service;1"`` 33 34 - Provides methods accessing "storage" objects – see `nsICacheStorage` below – giving further access to cache entries – see :ref:`nsICacheEntry <nsICacheEntry>` more below – per specific URL. 35 36 - Currently we have 3 types of storages, all the access methods return 37 an :ref:`nsICacheStorage <nsICacheStorage>` object: 38 39 - **memory-only** (``memoryCacheStorage``): stores data only in a 40 memory cache, data in this storage are never put to disk 41 42 - **disk** (``diskCacheStorage``): stores data on disk, but for 43 existing entries also looks into the memory-only storage; when 44 instructed via a special argument also primarily looks into 45 application caches 46 47 .. note:: 48 49 **application cache** (``appCacheStorage``): when a consumer has a 50 specific ``nsIApplicationCache`` (i.e. a particular app cache 51 version in a group) in hands, this storage will provide read and 52 write access to entries in that application cache; when the app 53 cache is not specified, this storage will operate over all 54 existing app caches. **This kind of storage is deprecated and will be removed** in `bug 1694662 <https://bugzilla.mozilla.org/show_bug.cgi?id=1694662>`_ 55 56 - The service also provides methods to clear the whole disk and memory 57 cache content or purge any intermediate memory structures: 58 59 - ``clear``– after it returns, all entries are no longer accessible 60 through the cache APIs; the method is fast to execute and 61 non-blocking in any way; the actual erase happens in background 62 63 - ``purgeFromMemory``– removes (schedules to remove) any 64 intermediate cache data held in memory for faster access (more 65 about the :ref:`Intermediate_Memory_Caching <Intermediate_Memory_Caching>` below) 66 67 .. _nsILoadContextInfo: 68 69 nsILoadContextInfo 70 ------------------ 71 72 - Distinguishes the scope of the storage demanded to open. 73 74 - Mandatory argument to ``*Storage`` methods of :ref:`nsICacheStorageService <nsICacheStorageService>`. 75 76 - `nsILoadContextInfo.idl (searchfox) <https://searchfox.org/mozilla-central/source/netwerk/base/nsILoadContextInfo.idl>`_ 77 78 79 - It is a helper interface wrapping following four arguments into a single one: 80 81 - **private-browsing** boolean flag 82 - **anonymous load** boolean flag 83 - **origin attributes** js value 84 85 .. note:: 86 87 Helper functions to create nsILoadContextInfo objects: 88 89 - C++ consumers: functions at ``LoadContextInfo.h`` exported 90 header 91 92 - JS consumers: ``Services.loadContextInfo`` which is an instance of ``nsILoadContextInfoFactory``. 93 94 - Two storage objects created with the same set of 95 ``nsILoadContextInfo``\ arguments are identical, containing the same 96 cache entries. 97 98 - Two storage objects created with in any way different 99 ``nsILoadContextInfo``\ arguments are strictly and completely 100 distinct and cache entries in them do not overlap even when having 101 the same URIs. 102 103 .. _nsICacheStorage: 104 105 nsICacheStorage 106 --------------- 107 108 - `nsICacheStorage.idl (searchfox) <https://searchfox.org/mozilla-central/source/netwerk/cache2/nsICacheStorage.idl>`_ 109 110 - Obtained from call to one of the ``*Storage`` methods on 111 :ref:`nsICacheStorageService <nsICacheStorageService>`. 112 113 - Represents a distinct storage area (or scope) to put and get cache 114 entries mapped by URLs into and from it. 115 116 - *Similarity with the old cache*\ : this interface may be with some 117 limitations considered as a mirror to ``nsICacheSession``, but less 118 generic and not inclining to abuse. 119 120 nsICacheEntryOpenCallback 121 ------------------------- 122 123 - `nsICacheEntryOpenCallback.idl (searchfox) <https://searchfox.org/mozilla-central/source/netwerk/cache2/nsICacheEntryOpenCallback.idl>`_ 124 125 - The result of ``nsICacheStorage.asyncOpenURI`` is always and only 126 sent to callbacks on this interface. 127 128 - These callbacks are ensured to be invoked when ``asyncOpenURI`` 129 returns ``NS_OK``. 130 131 - 132 133 .. note:: 134 135 When the 136 cache entry object is already present in memory or open as 137 "force-new" (a.k.a "open-truncate") this callback is invoked 138 sooner then the ``asyncOpenURI``\ method returns (i.e. 139 immediately); there is currently no way to opt out of this feature 140 (see `bug 141 938186 <https://bugzilla.mozilla.org/show_bug.cgi?id=938186>`__). 142 143 .. _nsICacheEntry: 144 145 nsICacheEntry 146 ------------- 147 148 - `nsICacheEntry.idl (searchfox) <https://searchfox.org/mozilla-central/source/netwerk/cache2/nsICacheEntry.idl>`_ 149 150 - Obtained asynchronously or pseudo-asynchronously by a call to 151 ``nsICacheStorage.asyncOpenURI``. 152 153 - Provides access to a cached entry data and meta data for reading or 154 writing or in some cases both, see below. 155 156 Lifetime of a new entry 157 ----------------------- 158 159 - Such entry is initially empty (no data or meta data is stored in it). 160 161 - The ``aNew``\ argument in ``onCacheEntryAvailable`` is ``true`` for 162 and only for new entries. 163 164 - Only one consumer (the so called "*writer*") may have such an entry 165 available (obtained via ``onCacheEntryAvailable``). 166 167 - Other parallel openers of the same cache entry are blocked (wait) for 168 invocation of their ``onCacheEntryAvailable`` until one of the 169 following occurs: 170 171 - The *writer* simply throws the entry away: other waiting opener in 172 line gets the entry again as "*new*", the cycle repeats. 173 174 .. note:: 175 176 This applies in general, writers throwing away the cache entry 177 means a failure to write the cache entry and a new writer is 178 being looked for again, the cache entry remains empty (a.k.a. 179 "new"). 180 181 - The *writer* stored all necessary meta data in the cache entry and 182 called ``metaDataReady`` on it: other consumers now get the entry 183 and may examine and potentially modify the meta data and read the 184 data (if any) of the cache entry. 185 - When the *writer* has data (i.e. the response payload) to write to 186 the cache entry, it **must** open the output stream on it 187 **before** it calls ``metaDataReady``. 188 189 - When the *writer* still keeps the cache entry and has open and keeps 190 open the output stream on it, other consumers may open input streams 191 on the entry. The data will be available as the *writer* writes data 192 to the cache entry's output stream immediately, even before the 193 output stream is closed. This is called :ref:`concurrent 194 read/write <Concurrent_read_and_write>`. 195 196 .. _Concurrent_read_and_write: 197 198 Concurrent read and write 199 ------------------------- 200 201 The cache supports reading a cache entry data while it is still being 202 written by the first consumer - the *writer*. 203 This can only be engaged for resumable responses that (`bug 204 960902 <https://bugzilla.mozilla.org/show_bug.cgi?id=960902#c17>`__) 205 don't need revalidation. Reason is that when the writer is interrupted 206 (by e.g. external canceling of the loading channel) concurrent readers 207 would not be able to reach the remaining unread content. 208 209 .. note:: 210 211 This could be improved by keeping the network load running and being 212 stored to the cache entry even after the writing channel has been 213 canceled. 214 215 When the *writer* is interrupted, the first concurrent *reader* in line 216 does a range request for the rest of the data - and becomes that way a 217 new *writer*. The rest of the *readers* are still concurrently reading 218 the content since output stream for the cache entry is again open and 219 kept by the current *writer*. 220 221 Lifetime of an existing entry with only a partial content 222 --------------------------------------------------------- 223 224 - Such a cache entry is first examined in the 225 ``nsICacheEntryOpenCallback.onCacheEntryCheck`` callback, where it 226 has to be checked for completeness. 227 - In this case, the ``Content-Length`` (or different indicator) header 228 doesn't equal to the data size reported by the cache entry. 229 - The consumer then indicates the cache entry needs to be revalidated 230 by returning ``ENTRY_NEEDS_REVALIDATION``\ from 231 ``onCacheEntryCheck``. 232 - This consumer, from the point of view the cache, takes a role of the 233 *writer*. 234 - Other parallel consumers, if any, are blocked until the *writer* 235 calls ``setValid`` on the cache entry. 236 - The consumer is then responsible to validate the partial content 237 cache entry with the network server and attempt to load the rest of 238 the data. 239 - When the server responds positively (in case of an HTTP server with a 240 206 response code) the *writer* (in this order) opens the output 241 stream on the cache entry and calls ``setValid`` to unblock other 242 pending openers. 243 - Concurrent read/write is engaged. 244 245 Lifetime of an existing entry that doesn't pass server revalidation 246 ------------------------------------------------------------------- 247 248 - Such a cache entry is first examined in the 249 ``nsICacheEntryOpenCallback.onCacheEntryCheck`` callback, where the 250 consumer finds out it must be revalidated with the server before use. 251 - The consumer then indicates the cache entry needs to be revalidated 252 by returning ``ENTRY_NEEDS_REVALIDATION``\ from 253 ``onCacheEntryCheck``. 254 - This consumer, from the point of view the cache, takes a role of the 255 *writer*. 256 - Other parallel consumers, if any, are blocked until the *writer* 257 calls ``setValid`` on the cache entry. 258 - The consumer is then responsible to validate the partial content 259 cache entry with the network server. 260 - The server responses with a 200 response which means the cached 261 content is no longer valid and a new version must be loaded from the 262 network. 263 - The *writer* then calls ``recreate``\ on the cache entry. This 264 returns a new empty entry to write the meta data and data to, the 265 *writer* exchanges its cache entry by this new one and handles it as 266 a new one. 267 - The *writer* then (in this order) fills the necessary meta data of 268 the cache entry, opens the output stream on it and calls 269 ``metaDataReady`` on it. 270 - Any other pending openers, if any, are now given this new entry to 271 examine and read as an existing entry. 272 273 Adding a new storage 274 -------------------- 275 276 Should there be a need to add a new distinct storage for which the 277 current scoping model would not be sufficient - use one of the two 278 following ways: 279 280 #. *[preferred]* Add a new ``<Your>Storage`` method on 281 :ref:`nsICacheStorageService <nsICacheStorageService>` and if needed give it any arguments to 282 specify the storage scope even more. Implementation only should need 283 to enhance the context key generation and parsing code and enhance 284 current - or create new when needed - :ref:`nsICacheStorage <nsICacheStorage>` 285 implementations to carry any additional information down to the cache 286 service. 287 #. *[*\ **not**\ *preferred]* Add a new argument to 288 :ref:`nsILoadContextInfo <nsILoadContextInfo>`; **be careful 289 here**, since some arguments on the context may not be known during 290 the load time, what may lead to inter-context data leaking or 291 implementation problems. Adding more distinction to 292 :ref:`nsILoadContextInfo <nsILoadContextInfo>` also affects all existing storages which may 293 not be always desirable. 294 295 See context keying details for more information. 296 297 Threading 298 --------- 299 300 The cache API is fully thread-safe. 301 302 The cache is using a single background thread where any IO operations 303 like opening, reading, writing and erasing happen. Also memory pool 304 management, eviction, visiting loops happen on this thread. 305 306 The thread supports several priority levels. Dispatching to a level with 307 a lower number is executed sooner then dispatching to higher number 308 layers; also any loop on lower levels yields to higher levels so that 309 scheduled deletion of 1000 files will not block opening cache entries. 310 311 #. **OPEN_PRIORITY:** except opening priority cache files also file 312 dooming happens here to prevent races 313 #. **READ_PRIORITY:** top level documents and head blocking script cache 314 files are open and read as the first 315 #. **OPEN** 316 #. **READ:** any normal priority content, such as images are open and 317 read here 318 #. **WRITE:** writes are processed as last, we cache data in memory in 319 the mean time 320 #. **MANAGEMENT:** level for the memory pool and CacheEntry background 321 operations 322 #. **CLOSE:** file closing level 323 #. **INDEX:** index is being rebuild here 324 #. **EVICT:** files overreaching the disk space consumption limit are 325 being evicted here 326 327 NOTE: Special case for eviction - when an eviction is scheduled on the 328 IO thread, all operations pending on the OPEN level are first merged to 329 the OPEN_PRIORITY level. The eviction preparation operation - i.e. 330 clearing of the internal IO state - is then put to the end of the 331 OPEN_PRIORITY level. All this happens atomically. 332 333 Storage and entries scopes 334 -------------------------- 335 336 A *scope key* string used to map the storage scope is based on the 337 arguments of :ref:`nsILoadContextInfo <nsILoadContextInfo>`. The form is following (currently 338 pending in `bug 339 968593 <https://bugzilla.mozilla.org/show_bug.cgi?id=968593>`__): 340 341 .. code:: JavaScript 342 343 a,b,i1009,p, 344 345 - Regular expression: ``(.([-,]+)?,)*`` 346 - The first letter is an identifier, identifiers are to be 347 alphabetically sorted and always terminate with ',' 348 - a - when present the scope is belonging to an **anonymous** load 349 - b - when present the scope is **in browser element** load 350 - i - when present must have a decimal integer value that represents an 351 app ID the scope belongs to, otherwise there is no app (app ID is 352 considered ``0``) 353 - p - when present the scope is of a **private browsing** load, this 354 never persists 355 356 ``CacheStorageService``\ keeps a global hashtable mapped by the *scope 357 key*. Elements in this global hashtable are hashtables of cache entries. 358 The cache entries are mapped by concantation of Enhance ID and URI 359 passed to ``nsICacheStorage.asyncOpenURI``. So that when an entry is 360 being looked up, first the global hashtable is searched using the 361 *scope key*. An entries hashtable is found. Then this entries hashtable 362 is searched using <enhance-id:><uri> string. The elements in this 363 hashtable are CacheEntry classes, see below. 364 365 The hash tables keep a strong reference to ``CacheEntry`` objects. The 366 only way to remove ``CacheEntry`` objects from memory is by exhausting a 367 memory limit for :ref:`Intermediate_Memory_Caching <Intermediate_Memory_Caching>`, what triggers a background 368 process of purging expired and then least used entries from memory. 369 Another way is to directly call the 370 ``nsICacheStorageService.purge``\ method. That method is also called 371 automatically on the ``"memory-pressure"`` indication. 372 373 Access to the hashtables is protected by a global lock. We also - in a 374 thread-safe manner - count the number of consumers keeping a reference 375 on each entry. The open callback actually doesn't give the consumer 376 directly the ``CacheEntry`` object but a small wrapper class that 377 manages the 'consumer reference counter' on its cache entry. This both 378 mechanisms ensure thread-safe access and also inability to have more 379 then a single instance of a ``CacheEntry`` for a single 380 <scope+enhanceID+URL> key. 381 382 ``CacheStorage``, implementing the :ref:`nsICacheStorage <nsICacheStorage>` interface, is 383 forwarding all calls to internal methods of ``CacheStorageService`` 384 passing itself as an argument. ``CacheStorageService`` then generates 385 the *scope key* using the ``nsILoadContextInfo`` of the storage. Note: 386 CacheStorage keeps a thread-safe copy of ``nsILoadContextInfo`` passed 387 to a ``*Storage`` method on ``nsICacheStorageService``. 388 389 Invoking open callbacks 390 ----------------------- 391 392 ``CacheEntry``, implementing the ``nsICacheEntry`` interface, is 393 responsible for managing the cache entry internal state and to properly 394 invoke ``onCacheEntryCheck`` and ``onCacheEntryAvaiable`` callbacks to 395 all callers of ``nsICacheStorage.asyncOpenURI``. 396 397 - Keeps a FIFO of all openers. 398 - Keeps its internal state like NOTLOADED, LOADING, EMPTY, WRITING, 399 READY, REVALIDATING. 400 - Keeps the number of consumers keeping a reference to it. 401 - Refers a ``CacheFile`` object that holds actual data and meta data 402 and, when told to, persists it to the disk. 403 404 The openers FIFO is an array of ``CacheEntry::Callback`` objects. 405 ``CacheEntry::Callback`` keeps a strong reference to the opener plus the 406 opening flags. ``nsICacheStorage.asyncOpenURI`` forwards to 407 ``CacheEntry::AsyncOpen`` and triggers the following pseudo-code: 408 409 **CacheStorage::AsyncOpenURI** - the API entry point: 410 411 - globally atomic: 412 413 - look a given ``CacheEntry`` in ``CacheStorageService`` hash tables 414 up 415 - if not found: create a new one, add it to the proper hash table 416 and set its state to NOTLOADED 417 - consumer reference ++ 418 419 - call to `CacheEntry::AsyncOpen` 420 - consumer reference -- 421 422 **CacheEntry::AsyncOpen** (entry atomic): 423 424 - the opener is added to FIFO, consumer reference ++ (dropped back 425 after an opener is removed from the FIFO) 426 - state == NOTLOADED: 427 428 - state = LOADING 429 - when OPEN_TRUNCATE flag was used: 430 431 - ``CacheFile`` is created as 'new', state = EMPTY 432 433 - otherwise: 434 435 - ``CacheFile`` is created and load on it started 436 - ``CacheEntry::OnFileReady`` notification is now expected 437 438 - state == LOADING: just do nothing and exit 439 - call to `CacheEntry::InvokeCallbacks` 440 441 **CacheEntry::InvokeCallbacks** (entry atomic): 442 443 - called on: 444 445 - a new opener has been added to the FIFO via an ``AsyncOpen`` call 446 - asynchronous result of CacheFile open ``CacheEntry::OnFileReady>`` 447 - the writer throws the entry away - ``CacheEntry::OnHandleClosed`` 448 - the **output stream** of the entry has been **opened** or 449 **closed** 450 - ``metaDataReady``\ or ``setValid``\ on the entry has been called 451 - the entry has been **doomed** 452 453 - state == EMPTY: 454 455 - on OPER_READONLY flag use: onCacheEntryAvailable with 456 ``null``\ for the cache entry 457 - otherwise: 458 459 - state = WRITING 460 - opener is removed from the FIFO and remembered as the current 461 '*writer*' 462 - onCacheEntryAvailable with ``aNew = true``\ and this entry is 463 invoked (on the caller thread) for the *writer* 464 465 - state == READY: 466 467 - onCacheEntryCheck with the entry is invoked on the first opener in 468 FIFO - on the caller thread if demanded 469 - result == RECHECK_AFTER_WRITE_FINISHED: 470 471 - opener is left in the FIFO with a flag ``RecheckAfterWrite`` 472 - such openers are skipped until the output stream on the entry 473 is closed, then ``onCacheEntryCheck`` is re-invoked on them 474 - Note: here is a potential for endless looping when 475 RECHECK_AFTER_WRITE_FINISHED is abused 476 477 - result == ENTRY_NEEDS_REVALIDATION: 478 479 - state = REVALIDATING, this prevents invocation of any callback 480 until ``CacheEntry::SetValid`` is called 481 - continue as in state ENTRY_WANTED (just below) 482 483 - result == ENTRY_WANTED: 484 485 - consumer reference ++ (dropped back when the consumer releases 486 the entry) 487 - onCacheEntryAvailable is invoked on the opener with 488 ``aNew = false``\ and the entry 489 - opener is removed from the FIFO 490 491 - result == ENTRY_NOT_WANTED: 492 493 - ``onCacheEntryAvailable`` is invoked on the opener with 494 ``null``\ for the entry 495 - opener is removed from the FIFO 496 497 - state == WRITING or REVALIDATING: 498 499 - do nothing and exit 500 501 - any other value of state is unexpected here (assertion failure) 502 - loop this process while there are openers in the FIFO 503 504 **CacheEntry::OnFileReady** (entry atomic): 505 506 - load result == failure or the file has not been found on disk (is 507 new): state = EMPTY 508 - otherwise: state = READY since the cache file has been found and is 509 usable containing meta data and data of the entry 510 - call to ``CacheEntry::InvokeCallbacks`` 511 512 **CacheEntry::OnHandleClosed** (entry atomic): 513 514 - Called when any consumer throws the cache entry away 515 - If the handle is not the handle given to the current *writer*, then 516 exit 517 - state == WRITING: the writer failed to call ``metaDataReady`` on the 518 entry - state = EMPTY 519 - state == REVALIDATING: the writer failed the re-validation process 520 and failed to call ``setValid`` on the entry - state = READY 521 - call to ``CacheEntry::InvokeCallbacks`` 522 523 **All consumers release the reference:** 524 525 - the entry may now be purged (removed) from memory when found expired 526 or least used on overrun of the :ref:`memory 527 pool <Intermediate_Memory_Caching>` limit 528 - when this is a disk cache entry, its cached data chunks are released 529 from memory and only meta data is kept 530 531 .. _Intermediate_Memory_Caching: 532 533 Intermediate memory caching 534 --------------------------- 535 536 Intermediate memory caching of frequently used metadata (a.k.a. disk cache memory pool). 537 538 For the disk cache entries we keep some of the most recent and most used 539 cache entries' meta data in memory for immediate zero-thread-loop 540 opening. The default size of this meta data memory pool is only 250kB 541 and is controlled by a new ``browser.cache.disk.metadata_memory_limit`` 542 preference. When the limit is exceeded, we purge (throw away) first 543 **expired** and then **least used** entries to free up memory again. 544 545 Only ``CacheEntry`` objects that are already loaded and filled with data 546 and having the 'consumer reference == 0' (`bug 547 942835 <https://bugzilla.mozilla.org/show_bug.cgi?id=942835#c3>`__) can 548 be purged. 549 550 The 'least used' entries are recognized by the lowest value of 551 `frecency <https://wiki.mozilla.org/User:Jesse/NewFrecency?title=User:Jesse/NewFrecency>`__ 552 we re-compute for each entry on its every access. The decay time is 553 controlled by the ``browser.cache.frecency_half_life_hours`` preference 554 and defaults to 6 hours. The best decay time will be based on results of 555 `an experiment <https://bugzilla.mozilla.org/show_bug.cgi?id=986728>`__. 556 557 The memory pool is represented by two lists (strong referring ordered 558 arrays) of ``CacheEntry`` objects: 559 560 #. Sorted by expiration time (that default to 0xFFFFFFFF) 561 #. Sorted by frecency (defaults to 0) 562 563 We have two such pools, one for memory-only entries actually 564 representing the memory-only cache and one for disk cache entries for 565 which we only keep the meta data. Each pool has a different limit 566 checking - the memory cache pool is controlled by 567 ``browser.cache.memory.capacity``, the disk entries pool is already 568 described above. The pool can be accessed and modified only on the cache 569 background thread. 570 571 Compression Dictionaries 572 --------------------------- 573 574 Compression Dictionaries are specced by the IETF: 575 https://datatracker.ietf.org/doc/draft-ietf-httpbis-compression-dictionary/ 576 577 See also: https://developer.chrome.com/blog/shared-dictionary-compression 578 and https://github.com/WICG/compression-dictionary-transport 579 580 Gecko's design for compression dictionary support: 581 582 We have special dict:<origin> entries with a listing of all dictionaries 583 for that origin, stored in metadata. 584 585 When a fetch is made, we check if there's a dict:<origin> cache entry. If 586 not, we know there are no dictionaries. If there is an entry, and we 587 haven't previously loaded it into memory, we read and parse the metadata 588 and create in-memory structures for all dictionaries for <origin>. This 589 includes the data needed to match and decide if we want to send a 590 "Available-Dictionary:" header with the request. 591 592 If a response to any request is received and it has a "Use-As-Dictionary" 593 header, we create a new dictionary entry in-memory and flag it for saving 594 to the dict:<origin> metadata. We set the stream up to decompress before 595 storing into the cache (see later options for alternatives in the future), 596 so that we can be ensured to be able to decompress later. We start 597 accumulating a hash value for the metadata entry. Once the resource is 598 fully received, we finalize the hash value and the metadata can be written. 599 600 When a response is received with dcb or dcz compress (dictionaries), we use 601 the cache entry for the dictionary that we sent in Available-Dictionary to 602 decompress the resource. This means reading it into memory and then 603 allowing the decompression to occur. 604 605 Several of these actions require a level of asynchronous action (waiting 606 for a cache entry to be loaded for use as a dictionary, or waiting for a 607 dict:<origin> entry to be loaded. This is generally handled via lambdas. 608 609 The metadata and in-memory entries are kept in sync with the cache by 610 clearing entries out when cache entries are Doomed. This also interacts 611 with Clear Site Data and cookie clear headers (see IETF spec). 612 613 Dictionary loading can also be triggered via <link rel="Compression 614 Dictionary" ...> and link headers. These will cause prefetches of the 615 dictionaries. 616 617 Things to watch on landing: 618 - Cache hitrate 619 - dictionary utilization 620 -- Add probes 621 - pageload metrics 622 -- Would require OHTTP-based collection 623 624 Future optimizations: 625 - Compressing dictionaries with zstd in the cache 626 -- Trades CPU use and some latency decoding dictionary-encoded files for hitrate 627 -- Perhaps only above some size 628 - Compressing dictionary-encoded files with zstd in the cache 629 -- Trades CPU use for hitrate 630 -- Perhaps only above some size 631 - Preemptively reading dict:<origin> entries into memory in the background at startup 632 -- Up to some limit 633 - LRU-ing dict:<origin> entries and dropping old ones from memory