Skip to content
This repository was archived by the owner on Feb 16, 2026. It is now read-only.

Commit bb1259f

Browse files
committed
doc: Explain vcl_refresh_* better
as suggested by Dridi here: #4400 (comment)
1 parent 671b93b commit bb1259f

2 files changed

Lines changed: 103 additions & 0 deletions

File tree

doc/sphinx/reference/vcl_step.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,8 @@ the stale ones.
345345
The returned value only affects response headers, the body that is delivered
346346
is always the one from the stale object.
347347

348+
See :ref:`vcl-built-in-refresh` for additional explanations.
349+
348350
|
349351
| ``merge``
350352
| Merge the headers we got from the backend response with

doc/sphinx/users-guide/vcl-built-in-code.rst

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,107 @@ This granularity, and the general goal of the built-in subroutines split
7070
is to allow to circumvent a specific aspect of the default rules without
7171
giving the entire logic up.
7272

73+
Specific split built-in subroutines
74+
-----------------------------------
75+
76+
Some split subroutines in the built-in VCL deserve additional explanations:
77+
78+
.. _vcl-built-in-refresh:
79+
80+
vcl_refresh_*
81+
~~~~~~~~~~~~~
82+
83+
These subroutines handle edge cases of backend refreshes. The precondition for
84+
these to be entered implicitly or explicitly via ``vcl_backend_refresh`` is that
85+
the current backend request can potentially create a cache object (that is, it
86+
is not for a private object as created by a pass or hit-for-pass) and that a
87+
stale object was found in cache which is not already invalidated. If this is the
88+
case, core code constructs a conditional ``GET`` request with the
89+
``If-Modified-Since`` and/or ``If-None-Match`` headers set before
90+
``vcl_backend_fetch`` is entered. If the VCL code does not remove the headers,
91+
the backend might respond with a ``304 Not Modified`` status, in which case
92+
``vcl_backend_refresh`` is called on the response to decide what do do (see
93+
:ref:`vcl_backend_refresh` for reference) and, if the built-in VCL is reached,
94+
the subs documented below will be called via ``vcl_builtin_backend_refresh``.
95+
96+
vcl_refresh_valid
97+
~~~~~~~~~~~~~~~~~
98+
99+
``vcl_refresh_valid`` handles the case where the stale object to be revalidated
100+
by the 304 response got explicitly removed from the cache by a ban or purge
101+
while the backend request was in progress::
102+
103+
sub vcl_refresh_valid {
104+
if (!obj_stale.is_valid) {
105+
return (error(503, "Invalid object for refresh"));
106+
}
107+
}
108+
109+
The error is generated because alternative actions might require additional
110+
consideration. There are basically two options:
111+
112+
We can ignore the fact that the now successfully revalidated object was *just*
113+
invalidated by not falling through to the built-in VCL with this subroutine in
114+
the user VCL::
115+
116+
sub vcl_refresh_valid {
117+
return;
118+
}
119+
120+
This avoids the error but can potentially result in invalidations being
121+
ineffective.
122+
123+
The other option is to retry the backend request without the conditional request
124+
headers. This option is implicitly active whenever the user VCL results in a
125+
``return(retry)`` from ``vcl_backend_error``, because core code removes the
126+
conditional request headers if the stale object is found to be invalidated.
127+
128+
A variant of this option is an explicit retry for the case at hand::
129+
130+
sub vcl_refresh_valid {
131+
return (retry);
132+
}
133+
134+
To summarize, refreshes should work fine as long as there is at least one retry
135+
from ``vcl_backend_error`` for 503 errors. Additionally, VCL allows for
136+
customization if needed.
137+
138+
vcl_refresh_conditions
139+
~~~~~~~~~~~~~~~~~~~~~~
140+
141+
This sub safeguards against invalid 304 responses getting unnoticed::
142+
143+
sub vcl_refresh_conditions {
144+
if (!bereq.http.if-modified-since &&
145+
!bereq.http.if-none-match) {
146+
return (error(503, "Unexpected 304"));
147+
}
148+
}
149+
150+
A backend should not respond with a 304 if neither of the conditional request
151+
headers were present in the backend request.
152+
153+
vcl_refresh_status
154+
~~~~~~~~~~~~~~~~~~
155+
156+
This sub safeguards against accidental 304 responses if the stale object does
157+
not have a 200 status::
158+
159+
sub vcl_refresh_status {
160+
if (obj_stale.status != 200) {
161+
return (error(503, "Invalid object for refresh (status)"));
162+
}
163+
}
164+
165+
The background here is that the HTTP standards only allow refreshes of status
166+
200 objects, but Vinyl Cache core code allows to deliberately violate this. In
167+
such cases, the status check needs to be neutered by not running the built-in
168+
code using::
169+
170+
sub vcl_refresh_status {
171+
return;
172+
}
173+
73174
Built-in VCL reference
74175
----------------------
75176

0 commit comments

Comments
 (0)