Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions cache/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,27 @@ type LookupOptions struct {
// To indicates the ending offset to read from the cached object. A
// value of 0 means to read to the end of the object.
To uint64

// LegacyReturnWholeBody restores a legacy behavior around range requests.
//
// In SDK v1.4.2 and earlier, under certain circumstances the requested range
// (From/To) would be ignored. Specifically, if:
// - The lookup (reader) is concurrent with the writer of the body
// - The size of the cached item's body was not provided by the writer
// - The reader requests a specific range of the cached item's body
// (`From` and `To` are provided in LookupOptions or GetBodyOptions)
// then the core cache API would ignore the requested range and provide
// the entire body, instead of providing just the requested range.
//
// In SDK v1.4.3, the default behavior changed. In a concurrent read/write:
// - If From and To are nonzero, the reader will block until the start of
// the requested range has been provided by the writer.
// - Only the requested range will be returned to the reader.
//
// Note that the full body is still provided if the range is invalid.
//
// LegacyReturnWholeBody restores the v1.4.2 behavior.
LegacyReturnWholeBody bool
}

func abiLookupOptions(opts LookupOptions) (fastly.CacheLookupOptions, error) {
Expand All @@ -272,6 +293,8 @@ func abiLookupOptions(opts LookupOptions) (fastly.CacheLookupOptions, error) {
abiOpts.SetRequest(req)
}

abiOpts.SetAlwaysUseRequestedRange(!opts.LegacyReturnWholeBody)

return abiOpts, nil
}

Expand Down
61 changes: 61 additions & 0 deletions cache/core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,64 @@ func ExampleFound_GetRange() {

fmt.Printf("The cached value was: %s", cachedStr)
}

func ExampleLookupOptions_LegacyReturnWholeBody() {
const (
key = "my_key"
contents = "my cached object"
)

// Start an insert; this will be concurrent with the read.
// Data written to this handle is streamed into the Fastly cache.
w, err := core.Insert([]byte(key), core.WriteOptions{
TTL: time.Hour,
SurrogateKeys: []string{key},
Length: uint64(len(contents)),
})
if err != nil {
panic(err)
}

// With the write still outstanding, start a lookup for a specific range.
legacy, err := core.Lookup([]byte(key), core.LookupOptions{
From: 3,
To: 8,
LegacyReturnWholeBody: true,
})
if err != nil {
panic(err)
}
// With the write still outstanding, start a lookup for a specific range.
updated, err := core.Lookup([]byte(key), core.LookupOptions{
From: 3,
To: 8,
})
if err != nil {
panic(err)
}

// The read and write are concurrent, and with an unknown length.
// In the legacy mode, we'll see the whole body;
// in the updated mode, we'll see just the range.

// Finish writing the body:
if _, err := io.WriteString(w, contents); err != nil {
panic(err)
}

legacyStr, err := io.ReadAll(legacy.Body)
if err != nil {
panic(err)
}
updatedStr, err := io.ReadAll(updated.Body)
if err != nil {
panic(err)
}

if got, want := string(legacyStr), "my cached object"; got != want {
panic(fmt.Sprintf("got: %q, want: %q", got, want))
}
if got, want := string(updatedStr), "cached"; got != want {
panic(fmt.Sprintf("got: %q, want: %q", got, want))
}
}
8 changes: 8 additions & 0 deletions internal/abi/fastly/cache_guest.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ func (o *CacheLookupOptions) SetRequest(req *HTTPRequest) {
o.mask |= cacheLookupOptionsMaskRequestHeaders
}

func (o *CacheLookupOptions) SetAlwaysUseRequestedRange(alwaysUseRequestedRange bool) {
if alwaysUseRequestedRange {
o.mask |= cacheLookupOptionsMaskAlwaysUseRequestedRange
} else {
o.mask &= ^cacheLookupOptionsMaskAlwaysUseRequestedRange
}
}

type CacheGetBodyOptions struct {
opts cacheGetBodyOptions
mask cacheGetBodyOptionsMask
Expand Down
4 changes: 4 additions & 0 deletions internal/abi/fastly/hostcalls_noguest.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,10 @@ func (o *CacheLookupOptions) SetRequest(req *HTTPRequest) error {
return fmt.Errorf("not implemented")
}

func (o *CacheLookupOptions) SetAlwaysUseRequestedRange(alwaysUseRequestedRange bool) error {
return fmt.Errorf("not implemented")
}

func (o *CacheGetBodyOptions) From(from uint64) error {
return fmt.Errorf("not implemented")
}
Expand Down
7 changes: 5 additions & 2 deletions internal/abi/fastly/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -721,13 +721,16 @@ type cacheLookupOptions struct {
// (flags (@witx repr u32)
// $reserved
// $request_headers
// $service_id
// $always_use_requested_range
// )
// )
type cacheLookupOptionsMask prim.U32

const (
cacheLookupOptionsMaskReserved cacheLookupOptionsMask = 0b0000_0001 // $reserved
cacheLookupOptionsMaskRequestHeaders cacheLookupOptionsMask = 0b0000_0010 // $request_headers
cacheLookupOptionsMaskReserved cacheLookupOptionsMask = 0b0000_0001 // $reserved
cacheLookupOptionsMaskRequestHeaders cacheLookupOptionsMask = 0b0000_0010 // $request_headers
cacheLookupOptionsMaskAlwaysUseRequestedRange cacheLookupOptionsMask = 0b0000_1000 // $always_use_requested_range
)

// witx:
Expand Down