diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index b232995..a1553b2 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -36,7 +36,7 @@ jobs: - name: Install Viceroy ${{ env.VICEROY_VERSION }} shell: 'bash' env: - VICEROY_VERSION: 0.10.2 + VICEROY_VERSION: 0.11.0 run: | echo "Install Viceroy ${{ env.VICEROY_VERSION }}..." wget --no-verbose https://github.com/fastly/Viceroy/releases/download/v${{ env.VICEROY_VERSION }}/viceroy_v${{ env.VICEROY_VERSION }}_linux-amd64.tar.gz diff --git a/compute/compute.go b/compute/compute.go new file mode 100644 index 0000000..82c761b --- /dev/null +++ b/compute/compute.go @@ -0,0 +1,36 @@ +// Copyright 2024 Fastly, Inc. + +// Useful functions for interacting with the compute instance runtime. +package compute + +import ( + "time" + + "github.com/fastly/compute-sdk-go/internal/abi/fastly" +) + +// Get the amount of time taken on the vCPU. +// +// The resulting time is millisecond-accurate, but we recommend against +// comparing the absolute values returned across different runs (or builds) +// of the program. +// +// Because compute guests can run on a variety of different platforms, +// you should not necessarily expect these values to converge across +// different sessions. Instead, we strongly recommend using this value +// to look at the relative cost of various operations in your code base, +// by taking the time before and after a particular operation and then +// dividing this by the total amount of vCPU time your program takes. +// The resulting percentage should be relatively stable across different +// platforms, and useful in doing A/B testing. +func GetVCPUTime() (time.Duration, error) { + milliseconds, err := fastly.GetVCPUMilliseconds() + + if err != nil { + return 0, err + } + + result := time.Duration(milliseconds) * time.Millisecond + + return result, nil +} diff --git a/compute/doc.go b/compute/doc.go new file mode 100644 index 0000000..02b1600 --- /dev/null +++ b/compute/doc.go @@ -0,0 +1,5 @@ +// Copyright 2024 Fastly, Inc. + +// Package compute_runtime provides ways to reflect on the operation of the +// compute runtime in which your application is operating. +package compute diff --git a/integration_tests/compute_runtime/fastly.toml b/integration_tests/compute_runtime/fastly.toml new file mode 100644 index 0000000..011c87b --- /dev/null +++ b/integration_tests/compute_runtime/fastly.toml @@ -0,0 +1,11 @@ +# This file describes a Fastly Compute package. To learn more visit: +# https://developer.fastly.com/reference/fastly-toml/ + +authors = ["oss@fastly.com"] +description = "" +language = "other" +manifest_version = 2 +name = "compute_runtime" +service_id = "" + +[local_server] diff --git a/integration_tests/compute_runtime/main_test.go b/integration_tests/compute_runtime/main_test.go new file mode 100644 index 0000000..14d454a --- /dev/null +++ b/integration_tests/compute_runtime/main_test.go @@ -0,0 +1,51 @@ +//go:build ((tinygo.wasm && wasi) || wasip1) && !nofastlyhostcalls + +// Copyright 2024 Fastly, Inc. +package main + +import ( + "testing" + "time" + + "github.com/fastly/compute-sdk-go/compute" +) + +func TestGetVcpuMs(t *testing.T) { + start, err := compute.GetVCPUTime() + if err != nil { + t.Errorf("Couldn't get starting vcpu time") + } + + time.Sleep(1 * time.Second) + + end, err := compute.GetVCPUTime() + if err != nil { + t.Errorf("Couldn't get ending vcpu time") + } + + if end - start > (200 * time.Millisecond) { + t.Errorf("Sleeping shouldn't count as vcpu time!") + } + + now, err := compute.GetVCPUTime() + if err != nil { + t.Errorf("Couldn't get starting vcpu time (part 2)") + } + + var counter uint64 + + counter = 0 + next := now + for now == next { + new_next, err := compute.GetVCPUTime() + if err != nil { + t.Errorf("Couldn't get part 2's recheck of vcpu time") + } + next = new_next + counter += 1 + } + + if counter == 0 { + t.Errorf("It should take at least one loop to advance vcpu time") + } +} diff --git a/internal/abi/fastly/compute_runtime_guest.go b/internal/abi/fastly/compute_runtime_guest.go new file mode 100644 index 0000000..8e93118 --- /dev/null +++ b/internal/abi/fastly/compute_runtime_guest.go @@ -0,0 +1,45 @@ +//go:build ((tinygo.wasm && wasi) || wasip1) && !nofastlyhostcalls + +// Copyright 2024 Fastly, Inc. +// +package fastly + +import ( + "github.com/fastly/compute-sdk-go/internal/abi/prim" +) + +// witx: +// (module $fastly_compute_runtime +// +// (@interface func (export "get_vcpu_ms") +// (result $err (expected $vcpu_ms (error $fastly_status))) +// ) +// +// ) +// +//go:wasmimport fastly_compute_runtime get_vcpu_ms +//go:noescape +func fastlyGetVCPUMs(prim.Pointer[prim.U64]) FastlyStatus + +// Return the number of milliseconds spent on the CPU for the current +// session. +// +// Because compute guests can run on a variety of different platforms, +// you should not necessarily expect these values to converge across +// different sessions. Instead, we strongly recommend using this value +// to look at the relative cost of various operations in your code base, +// by taking the time before and after a particular operation and then +// dividing this by the total amount of vCPU time your program takes. +// The resulting percentage should be relatively stable across different +// platforms, and useful in doing A/B testing. +func GetVCPUMilliseconds() (uint64, error) { + var milliseconds prim.U64 + + err := fastlyGetVCPUMs(prim.ToPointer(&milliseconds)).toError() + + if err != nil { + return 0, err + } + + return uint64(milliseconds), nil +} diff --git a/internal/abi/fastly/hostcalls_noguest.go b/internal/abi/fastly/hostcalls_noguest.go index bb03ea1..253d057 100644 --- a/internal/abi/fastly/hostcalls_noguest.go +++ b/internal/abi/fastly/hostcalls_noguest.go @@ -512,3 +512,7 @@ func PenaltyBoxAdd(penaltyBox, entry string, ttl time.Duration) error { func PenaltyBoxHas(penaltyBox, entry string) (bool, error) { return false, fmt.Errorf("not implemented") } + +func GetVCPUMilliseconds() (uint64, error) { + return 0, fmt.Errorf("not implemented") +}