Skip to content

Commit

Permalink
Move to public Cache
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewBarba committed Sep 13, 2023
1 parent 5b6356e commit 806dd93
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 84 deletions.
84 changes: 84 additions & 0 deletions Sources/Compute/Cache.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//
// Cache.swift
//
//
// Created by Andrew Barba on 9/13/23.
//

public struct Cache: Sendable {

public static func getOrSet(_ key: String, _ handler: () async throws -> (FetchResponse, CachePolicy)) async throws -> Entry {
let trx = try await Fastly.Cache.getOrSet(key) {
let (res, cachePolicy) = try await handler()
guard let header = res.headers[.contentLength], let length = Int(header) else {
let bytes = try await res.bytes()
return (.bytes(bytes), cachePolicy)
}
return await (.body(res.body.body, length: length), cachePolicy)
}
return try .init(trx)
}

public static func getOrSet(_ key: String, _ handler: () async throws -> ([UInt8], CachePolicy)) async throws -> Entry {
let trx = try await Fastly.Cache.getOrSet(key) {
let (bytes, cachePolicy) = try await handler()
return (.bytes(bytes), cachePolicy)
}
return try .init(trx)
}

public static func getOrSet(_ key: String, _ handler: () async throws -> (String, CachePolicy)) async throws -> Entry {
let trx = try await Fastly.Cache.getOrSet(key) {
let (text, cachePolicy) = try await handler()
return (.bytes(.init(text.utf8)), cachePolicy)
}
return try .init(trx)
}

public static func getOrSet(_ key: String, _ handler: () async throws -> (Data, CachePolicy)) async throws -> Entry {
let trx = try await Fastly.Cache.getOrSet(key) {
let (data, cachePolicy) = try await handler()
return (.bytes(data.bytes), cachePolicy)
}
return try .init(trx)
}

public static func getOrSet(_ key: String, _ handler: () async throws -> ([String: Sendable], CachePolicy)) async throws -> Entry {
let trx = try await Fastly.Cache.getOrSet(key) {
let (json, cachePolicy) = try await handler()
let data = try JSONSerialization.data(withJSONObject: json)
return (.bytes(data.bytes), cachePolicy)
}
return try .init(trx)
}

public static func getOrSet(_ key: String, _ handler: () async throws -> ([Sendable], CachePolicy)) async throws -> Entry {
let trx = try await Fastly.Cache.getOrSet(key) {
let (json, cachePolicy) = try await handler()
let data = try JSONSerialization.data(withJSONObject: json)
return (.bytes(data.bytes), cachePolicy)
}
return try .init(trx)
}
}

extension Cache {

public struct Entry: Sendable {

public let body: ReadableBody

public let age: Int

public let hits: Int

public let contentLength: Int

internal init(_ trx: Fastly.Cache.Transaction) throws {
self.body = try ReadableWasiBody(trx.getBody())
self.age = try trx.getAge()
self.hits = try trx.getHits()
self.contentLength = try trx.getLength()
}
}
}
134 changes: 50 additions & 84 deletions Sources/Compute/Fastly/FastlyCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,61 +8,48 @@
import ComputeRuntime

extension Fastly {

public struct Cache: Sendable {

public static func getOrSet(_ key: String, _ handler: () async throws -> (FetchResponse, CachePolicy)) async throws -> ReadableBody {
return try await getOrSet(key) {
let (res, cachePolicy) = try await handler()
guard let header = res.headers[.contentLength], let length = Int(header) else {
let bytes = try await res.bytes()
return (.bytes(bytes), cachePolicy)
}
return await (.body(res.body.body, length: length), cachePolicy)
}
}
public static func getOrSet(_ key: String, _ handler: () async throws -> (HandlerData, CachePolicy)) async throws -> Transaction {
// Open the transaction
let trx = try Transaction.lookup(key)

public static func getOrSet(_ key: String, _ handler: () async throws -> ([UInt8], CachePolicy)) async throws -> ReadableBody {
return try await getOrSet(key) {
let (bytes, cachePolicy) = try await handler()
return (.bytes(bytes), cachePolicy)
}
}
// Get current transaction state
let state = try trx.getState()

public static func getOrSet(_ key: String, _ handler: () async throws -> (String, CachePolicy)) async throws -> ReadableBody {
return try await getOrSet(key) {
let (text, cachePolicy) = try await handler()
return (.bytes(.init(text.utf8)), cachePolicy)
// If state is usable then return the body from cache
if state == .found || state == .usable {
return trx
}
}

public static func getOrSet(_ key: String, _ handler: () async throws -> (Data, CachePolicy)) async throws -> ReadableBody {
return try await getOrSet(key) {
do {
// If its not usable then begin executing handler with new value
let (data, cachePolicy) = try await handler()
return (.bytes(data.bytes), cachePolicy)
}
}

public static func getOrSet(_ key: String, _ handler: () async throws -> ([String: Sendable], CachePolicy)) async throws -> ReadableBody {
return try await getOrSet(key) {
let (json, cachePolicy) = try await handler()
let data = try JSONSerialization.data(withJSONObject: json)
return (.bytes(data.bytes), cachePolicy)
}
}
// Get an instance to the insert handle
var writer = try trx.insertAndStreamBack(cachePolicy: cachePolicy, length: data.length)

// Append bytes from handler to writeable body
switch data {
case .body(let body, _):
try writer.body.append(body)
case .bytes(let bytes):
try writer.body.write(bytes)
}

public static func getOrSet(_ key: String, _ handler: () async throws -> ([Sendable], CachePolicy)) async throws -> ReadableBody {
return try await getOrSet(key) {
let (json, cachePolicy) = try await handler()
let data = try JSONSerialization.data(withJSONObject: json)
return (.bytes(data.bytes), cachePolicy)
return writer.transaction
} catch {
// Cancel the transaction if something went wrong
try trx.cancel()

// Rethrow original error
throw error
}
}
}
}

extension Fastly.Cache {

public enum HandlerData {
case body(_ body: Fastly.Body, length: Int)
case bytes(_ bytes: [UInt8])
Expand All @@ -76,45 +63,6 @@ extension Fastly.Cache {
}
}
}

public static func getOrSet(_ key: String, _ handler: () async throws -> (HandlerData, CachePolicy)) async throws -> ReadableBody {
// Open the transaction
let trx = try await Transaction.lookup(key)

// Get current transaction state
let state = try await trx.getState()

// If state is usable then return the body from cache
if state == .found || state == .usable {
let body = try await trx.getBody()
return ReadableWasiBody(body)
}

do {
// If its not usable then begin executing handler with new value
let (data, cachePolicy) = try await handler()

// Get an instance to the insert handle
var writer = try await trx.insertAndStreamBack(cachePolicy: cachePolicy, length: data.length)

// Append bytes from handler to writeable body
switch data {
case .body(let body, _):
try writer.body.append(body)
case .bytes(let bytes):
try writer.body.write(bytes)
}

// Get handle to reabable body out from cache
return try await ReadableWasiBody(writer.transaction.getBody())
} catch {
// Cancel the transaction if something went wrong
try await trx.cancel()

// Rethrow original error
throw error
}
}
}

extension Fastly.Cache {
Expand All @@ -126,15 +74,15 @@ extension Fastly.Cache {
self.handle = handle
}

public static func lookup(_ key: String) async throws -> Transaction {
public static func lookup(_ key: String) throws -> Transaction {
var handle: WasiHandle = 0
let options = CacheLookupOptions.reserved
var config = CacheLookupConfig()
try wasi(fastly_cache__cache_transaction_lookup(key, key.utf8.count, options.rawValue, &config, &handle))
return Transaction(handle)
}

public func insertAndStreamBack(cachePolicy: CachePolicy, length: Int) async throws -> (body: Fastly.Body, transaction: Transaction) {
public func insertAndStreamBack(cachePolicy: CachePolicy, length: Int) throws -> (body: Fastly.Body, transaction: Transaction) {
var bodyHandle: WasiHandle = 0
var cacheHandle: WasiHandle = 0
let options: CacheWriteOptions = [.initialAgeNs, .staleWhileRevalidateNs, .length]
Expand All @@ -146,21 +94,39 @@ extension Fastly.Cache {
return (.init(bodyHandle), .init(cacheHandle))
}

public func getState() async throws -> CacheState {
public func getState() throws -> CacheState {
var value: UInt8 = 0
try wasi(fastly_cache__cache_get_state(handle, &value))
return CacheState(rawValue: value)
}

public func getBody() async throws -> Fastly.Body {
public func getAge() throws -> Int {
var value: UInt64 = 0
try wasi(fastly_cache__cache_get_age_ns(handle, &value))
return .init(value / 1_000_000_000)
}

public func getLength() throws -> Int {
var value: UInt64 = 0
try wasi(fastly_cache__cache_get_length(handle, &value))
return .init(value)
}

public func getHits() throws -> Int {
var value: UInt64 = 0
try wasi(fastly_cache__cache_get_hits(handle, &value))
return .init(value)
}

public func getBody() throws -> Fastly.Body {
var bodyHandle: WasiHandle = 0
let options = CacheGetBodyOptions.reserved
var config = CacheGetBodyConfig()
try wasi(fastly_cache__cache_get_body(handle, options.rawValue, &config, &bodyHandle))
return .init(bodyHandle)
}

public func cancel() async throws {
public func cancel() throws {
try wasi(fastly_cache__cache_transaction_cancel(handle))
}
}
Expand Down
9 changes: 9 additions & 0 deletions Sources/ComputeRuntime/include/ComputeRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,15 @@ int fastly_cache__cache_transaction_cancel(WasiHandle handle);
WASM_IMPORT("fastly_cache", "get_state")
int fastly_cache__cache_get_state(WasiHandle handle, uint8_t *ret);

WASM_IMPORT("fastly_cache", "get_length")
int fastly_cache__cache_get_length(WasiHandle handle, uint64_t *ret);

WASM_IMPORT("fastly_cache", "get_age_ns")
int fastly_cache__cache_get_age_ns(WasiHandle handle, uint64_t *ret);

WASM_IMPORT("fastly_cache", "get_hits")
int fastly_cache__cache_get_hits(WasiHandle handle, uint64_t *ret);

WASM_IMPORT("fastly_cache", "get_body")
int fastly_cache__cache_get_body(WasiHandle handle, uint32_t options_mask,
CacheGetBodyConfig *config,
Expand Down

0 comments on commit 806dd93

Please sign in to comment.