Skip to content

Commit

Permalink
Merge branch 'main' into fp-3897-detect-openapi-definitions-directly-…
Browse files Browse the repository at this point in the history
…from-hono-middleware
  • Loading branch information
brettimus committed Dec 3, 2024
2 parents 44c91d7 + f1a2c26 commit 66a00c8
Show file tree
Hide file tree
Showing 11 changed files with 141 additions and 7 deletions.
11 changes: 11 additions & 0 deletions examples/service-bindings/meow/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,15 @@ app.get("/", async (c) => {
return c.text(`Meow from above! ☁️🪿🐈 (But dog says "${bark}")`);
});

app.get("/geese-to-bark-at", async (c) => {
const geeseResponse = await c.env.WOOF.fetch(c.req.raw);
try {
const geese = await geeseResponse.json();
console.log(geese);
return c.json(geese as Array<unknown>);
} catch (_e) {
return c.json({ error: "Failed to parse geese response as JSON" }, 500);
}
});

export default instrument(app);
4 changes: 2 additions & 2 deletions examples/service-bindings/woof/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export class WoofWorker extends WorkerEntrypoint {

// From Cloudflare docs: "Currently, entrypoints without a named handler are not supported"
// TODO - Check if this is still the case that we need a fetch handler?
async fetch() {
return new Response(null, { status: 404 });
async fetch(_request: Request | string) {
return fetch("https://placegoose.mies.workers.dev/geese");
}
}

Expand Down
7 changes: 6 additions & 1 deletion packages/client-library-otel/src/patch/cf-bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@ function proxyServiceBinding(o: object, bindingName: string) {
// The name for the span, which will show up in the UI
const name = `${bindingName}.${serviceMethod}`;

// For the `fetch` method, we need to bind `this` (the javascript `this` keyword) to the service target,
// otherwise `fetch` will fail!
// For RPC calls we do not need to do any special `this` binding
const shouldBindThis = serviceMethod === "fetch";

const measuredBinding = measure(
{
name,
Expand All @@ -181,7 +186,7 @@ function proxyServiceBinding(o: object, bindingName: string) {
},
onError: handleError,
},
serviceValue,
shouldBindThis ? serviceValue.bind(serviceTarget) : serviceValue,
);

return measuredBinding;
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion www/src/content/blog/2024-12-02-mega-launch-week.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ Read the full product post here:

## Tuesday, Dec 3rd: To be announced!

---

## Wednesday, Dec 4th: To be announced!

---

## Thursday, Dec 5th: To be announced!

---

## Friday, Dec 6th: To be announced!

116 changes: 116 additions & 0 deletions www/src/content/blog/2024-12-03-cf-service-bindings.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
---
title: "Introducing Tracing for Cloudflare Service Bindings with Fiberplane"
description: "Learn how Fiberplane's instrumentation client now supports tracing Cloudflare Service Bindings"
slug: introducing-tracing-for-cloudflare-service-bindings-with-fiberplane
date: 2024-12-03
author: Nele Uhlemann
tags:
- cloudflare
- typescript
- launch week
- services architecture
---

import { Aside, LinkCard } from "@astrojs/starlight/components";

Today, we are excited to announce a new feature in [Fiberplane's instrumentation client](https://fiberplane.com/docs/components/client-library/): support for tracing [Cloudflare Service Bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/).
Service bindings allow for Worker-to-Worker communication, and tracing them enables better visibility and debugging for distributed systems built on Cloudflare.

## Cloudflare Service Bindings

Cloudflare Workers can invoke other Cloudflare Workers in the same network, facilitating the creation of distributed architectures on Cloudflare.

Workers Service Bindings allow for multiple Workers to utilize another Worker's functionality, like authenticating requests or sending emails.

Decomposing your logic into multiple Workers can therefore reduce code duplication, simplify maintenance, and streamline updates to core parts of your system.

Additionally, when you rely on Service Bindings, certain Workers can remain completely unexposed to the public internet, which gives your application an nice added layer of security.

### Simple example use case

Imagine you want to organize a marathon event. To allow athletes to register, you provide a sign-up form on your website.

When an athlete submits the form, their data is stored in a database. An email is then sent to confirm their registration.

Separately, and not as part of the registration process, you offer a newsletter subscription.
When an athlete subscribes to the newsletter, their email address is stored in a different database, and they receive a welcome email.

In this scenario, you could have three workers: one for handling the sign-up form, another for managing the newsletter subscription, and then a _shared_ email worker to send emails.
![Example Workers architecture](@/assets/blog/2024-12-03-workers-example-architecture.png)

### How it works

When one Cloudflare Worker invokes another, the calls remain within the same execution thread.
This design ensures that breaking down service logic into multiple Workers does not introduce network latency, making the architecture both flexible and high-performance.

There are two primary ways Workers can communicate using Service Bindings:

- RPC
- HTTP

Bindings are configured in the `wrangler.toml` file. If you want to learn more about setting up Service Bindings, check out the [Cloudflare documentation](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/).
Since the calls occur in the same thread, it’s important to handle them asynchronously.
Otherwise, Worker A might terminate the thread before Worker B has completed its operation. Here is a simple example using rpc:

```typescript
await c.env.WORKER_EMAIL.send(email, firstName);
```

If the Service Binding is defined in the `wrangler.toml` file, the Worker can invoke another Worker’s function by using the binding name and the appropriate function from the other Worker.

## Developing with multiple Workers

As service distribution increases, so does the complexity of observing and debugging a system.
Tracing the full call chain of a request becomes critical to understanding performance and error handling.
This is particularly important when dealing with asynchronous background tasks executed by other Workers.

Observing and measuring response times is already an important aspect of local development.
It helps you understand your application’s performance and identify bottlenecks.

## Example with Fiberplane

Let’s explore how Fiberplane’s instrumentation client can help trace requests between Cloudflare Workers.

In this example, we use Fiberplane's client to instrument the sign-up Worker, which stores user data in a database and invokes the email Worker to send a confirmation email.
With Fiberplane, you can visualize the entire call chain of this request.

![Sequential Trace](@/assets/blog/2024-12-03-workers-binding-sequential.png)

In the trace above, we see a sequential execution model where the sign-up Worker first performs database operations and then invokes the email Worker.
The total execution time depends on the entire call chain, as shown below.

We can improve the response time by using parallel execution with `waitUntil()`.

```typescript
app.post("/api/marathon-wait-until", async (c) => {
const { firstName, lastName, email, address, distance } = await c.req.json();

//using waitUntil() and invoke email worker via rpc
c.executionCtx.waitUntil(c.env.WORKER_EMAIL.send(email, firstName));

await insertData(
firstName,
lastName,
email,
address,
distance,
c.env.DATABASE_URL
);

return c.text("Thanks for registering for our Marathon", 200);
});
```

Using Fiberplane, we can also visualize the improved execution model:

![WaitUntil() Trace](@/assets/blog/2024-12-03-workers-binding-waituntil.png)

By adopting `waitUntil()` for parallel execution, you can reduce user-facing latency while ensuring that background operations are completed efficiently.
Tracing across multiple Workers is therefore important to understand dependencies and visualize the total lifecycle of the thread.

Additionally, Fiberplane's studio provides detailed information about the Service Binding:

![Binding information](@/assets/blog/2024-12-03-workers-binding-information.png)

This new feature in Fiberplane's instrumentation client simplifies tracing between Cloudflare Workers, offering developers better visibility and control over distributed architectures.
Explore the [example code](https://github.com/Nlea/cloudflare-handling-asynchronous-tasks-/tree/seperate-workers/seperate-workers) and start tracing your Cloudflare Workers today!
6 changes: 3 additions & 3 deletions www/src/content/docs/docs/components/client-library.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ export default instrument(app, {
// Don't proxy `console.*` functions to send logging data to a local FPX server
logging: false,
// Don't proxy Cloudflare bindings automatically
cfBindings: false,
},
cfBindings: false
}
});
```

Expand All @@ -128,6 +128,6 @@ These will show up as spans in your request timeline in Fiberplane Studio, along

For instance, you can see the SQL query executed on D1, the R2 bucket that was accessed, or the arguments passed to the service binding to another Cloudflare Worker.

***
---

To explore more of what the client library can do, check out the code on [Github](https://github.com/fiberplane/fpx/tree/main/packages/client-library-otel).

0 comments on commit 66a00c8

Please sign in to comment.