Add platform abstraction layer with traits and RuntimeServices#545
Add platform abstraction layer with traits and RuntimeServices#545
Conversation
Rename crates/common → crates/trusted-server-core and crates/fastly → crates/trusted-server-adapter-fastly following the EdgeZero naming convention. Add EdgeZero workspace dependencies pinned to rev 170b74b. Update all references across docs, CI workflows, scripts, agent files, and configuration.
Introduces trusted-server-core::platform with PlatformConfigStore, PlatformSecretStore, PlatformKvStore, PlatformBackend, PlatformHttpClient, and PlatformGeo traits alongside ClientInfo, PlatformError, and RuntimeServices. Wires the Fastly adapter implementations and threads RuntimeServices into route_request. Moves GeoInfo to platform/types as platform-neutral data and adds geo_from_fastly for field mapping.
…o-pr2-platform-traits
- Defer KV store opening: replace early error return with a local UnavailableKvStore fallback so routes that do not need synthetic ID access succeed when the KV store is missing or temporarily unavailable - Use ConfigStore::try_open + try_get and SecretStore::try_get throughout FastlyPlatformConfigStore and FastlyPlatformSecretStore to honour the Result contract instead of panicking on open/lookup failure - Encapsulate RuntimeServices service fields as pub(crate) with public getter methods (config_store, secret_store, backend, http_client, geo) and a pub new() constructor; adapter updated to use new() - Reference #487 in FastlyPlatformHttpClient stub (PR 6 implements it) - Remove unused KvPage re-export from platform/mod.rs - Use super::KvHandle shorthand in RuntimeServices::kv_handle()
aram356
left a comment
There was a problem hiding this comment.
Summary
Solid architectural direction — this PR lays the groundwork for decoupling trusted-server-core from the Fastly SDK. The trait design, object-safety assertions, and graceful KV degradation are well done. A few issues to address before merge, mostly around constructor ergonomics and a question about Send bounds.
Blocking
🔧 wrench
RuntimeServices::newtakes 7 args with lint suppression: At the boundary today, will exceed it whencreative_store/counter_storeare added per the TODO. Use a builder pattern instead. (crates/trusted-server-core/src/platform/types.rs:103)- PR description overstates decoupling:
trusted-server-corestill depends onfastlydirectly (geo.rs,backend.rs,fastly_storage.rs,settings.rs). The description says this "enables future non-Fastly adapter support" — should clarify this is step N of M and thefastlydep removal comes in a follow-up.
❓ question
PlatformPendingRequestis!Send— intentional?:Box<dyn Any>is!Send, whilePlatformHttpClient: Send + Sync. Will this asymmetry hold for future adapters usingtokio::spawn? (crates/trusted-server-core/src/platform/http.rs:64)
Non-blocking
🤔 thinking
PlatformBackendSpecduplicatesBackendConfigfields: Both types havescheme,host,port,certificate_check,first_byte_timeout— one owns, the other borrows. Everyensurecall allocates owned Strings immediately borrowed. Consider sharing the type.- Asymmetric key semantics on
PlatformConfigStore:gettakesstore_name,put/deletetakestore_id. Newtype wrappers would make misuse a compile error. (crates/trusted-server-core/src/platform/traits.rs:19)
♻️ refactor
geo_from_fastlyshould live in adapter, not core: Importsfastly::geo::Geoin core, tying it to Fastly. Beingpubmeans new code could accidentally depend on it. (crates/trusted-server-core/src/geo.rs:10)UnavailableKvStoreshould live in core, not adapter: Platform-neutral fallback that any future adapter would duplicate. (crates/trusted-server-adapter-fastly/src/platform.rs:276)
⛏ nitpick
- Prefer
attach_withoverattach(format!(...)): Avoids allocation unless the report is inspected. Appears throughoutplatform.rs.
🌱 seedling
- No
Debugimpl forRuntimeServices: A manualDebugimpl printingclient_infowhile omitting service handles would help debugging.
CI Status
- fmt: PASS
- clippy: PASS
- rust tests: PASS
- js tests: PASS
| /// | ||
| /// The core stores this as an opaque support type. Adapter implementations can | ||
| /// recover their concrete runtime handle through [`Self::downcast`]. | ||
| pub struct PlatformPendingRequest { |
There was a problem hiding this comment.
❓ question — PlatformPendingRequest uses Box<dyn Any> which is !Send
PlatformPendingRequest stores Box<dyn Any> (!Send), while RuntimeServices stores Arc<dyn PlatformHttpClient> requiring Send + Sync. The trait contract says PlatformHttpClient: Send + Sync but async methods' futures are !Send (via #[async_trait(?Send)]).
Is this intentional asymmetry? Will it hold for future adapters (e.g., an Axum adapter using tokio::spawn)?
There was a problem hiding this comment.
Intentional — documented in the struct's doc comment. Box<dyn Any> (not Box<dyn Any + Send>) matches the #[async_trait(?Send)] contract: edgezero_core::body::Body wraps a LocalBoxStream that is !Send by design for wasm32 compatibility. A future multi-threaded adapter (e.g. Axum) would use Arc rather than relying on Send here.
|
Addressing the review findings: ♻️ 🤔 ⛏ All other blocking and non-blocking items have been addressed in the latest commits. |
Summary
trusted-server-core::platformmodule with six platform-neutral traits (PlatformConfigStore,PlatformSecretStore,PlatformKvStore,PlatformBackend,PlatformHttpClient,PlatformGeo) as the first step toward non-Fastly adapter support — full decoupling offastlyfrom core lands in Remove fastly from core crate #496ClientInfo(extract-once plain data struct),PlatformErrorenum, andRuntimeServicesaggregate that threads all platform services intoroute_requestGeoInfotoplatform/typesas platform-neutral data and adds ageo_from_fastlymapping function; wires full Fastly implementations in the adapter crateChanges
crates/trusted-server-core/src/platform/mod.rsRuntimeServices, re-exports, object-safety assertions, unit testscrates/trusted-server-core/src/platform/traits.rsasync_trait+ WASM-conditional?Sendcrates/trusted-server-core/src/platform/types.rsClientInfo,GeoInfo(moved here fromgeo.rs);RuntimeServicesBuilderreplacesnew()constructor;StoreName/StoreIdnewtypescrates/trusted-server-core/src/platform/error.rsPlatformErrorenumcrates/trusted-server-core/src/platform/http.rsPlatformHttpClientrequest/response typescrates/trusted-server-core/src/platform/kv.rsUnavailableKvStoreplatform-neutral fallbackcrates/trusted-server-core/src/geo.rsplatform/types, addsgeo_from_fastlycrates/trusted-server-core/src/backend.rsPlatformBackendtraitcrates/trusted-server-core/src/fastly_storage.rsPlatformKvStore/PlatformConfigStoreimpl wiringcrates/trusted-server-adapter-fastly/src/platform.rstry_open/try_getthroughoutcrates/trusted-server-adapter-fastly/src/main.rsRuntimeServices, threads intoroute_request; KV store failure degrades gracefullycrates/trusted-server-adapter-fastly/Cargo.tomlasync-trait,futuresdependenciescrates/trusted-server-core/Cargo.tomlasync-traitdependencyCloses
Closes #483
Test plan
cargo test --workspacecargo clippy --workspace --all-targets --all-features -- -D warningscargo fmt --all -- --checkcd crates/js/lib && npx vitest runcd crates/js/lib && npm run formatcd docs && npm run formatcargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1fastly compute serveChecklist
unwrap()in production code — useexpect("should ...")logmacros (notprintln!)