R2 access seems to be quite slow

I copied the example for exposing an R2 bucket to the internet here: Using R2 in a Worker · Cloudflare R2 docs

When I try to retrieve assets using this worker, often the response times are around the 0.5s-1s mark (sometimes more).

For example, this is a gif that is stored in my R2 bucket and retrieved using that worker: https://bacon-cdn.andrewdjessop.workers.dev/shakes-fists.gif

I’ve just fetched this 10 times and it seems the average is around 700ms - 800ms. I was expecting something more around 100ms (preferably less).

Is this a fundamental limitation of R2, or is it just because it’s in beta and is not rolled out to all data centres, or something else to do with the code?

Cheers.

Edit: here’s one of the logs:

outcome": "ok",
  "scriptName": null,
  "exceptions": [],
  "logs": [
    {
      "message": [
        "GET object shakes-fists.gif: https://bacon-cdn.andrewdjessop.workers.dev/shakes-fists.gif"
      ],
      "level": "log",
      "timestamp": 1653553449825
    }
  ],
  "eventTimestamp": 1653553449825,
  "event": {
    "request": {
      "url": "https://bacon-cdn.andrewdjessop.workers.dev/shakes-fists.gif",
      "method": "GET",
      "headers": {
        "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
        "accept-encoding": "gzip",
        "accept-language": "en-GB,en-US;q=0.9,en;q=0.8",
        "cache-control": "no-cache",
        "cf-connecting-ip": "8.20.126.63",
        "cf-ipcountry": "DE",
        "cf-ray": "71151ce55a7b994b",
        "cf-visitor": "{\"scheme\":\"https\"}",
        "connection": "Keep-Alive",
        "host": "bacon-cdn.andrewdjessop.workers.dev",
        "pragma": "no-cache",
        "sec-ch-ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"100\", \"Google Chrome\";v=\"100\"",
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-platform": "\"macOS\"",
        "sec-fetch-dest": "document",
        "sec-fetch-mode": "navigate",
        "sec-fetch-site": "none",
        "sec-fetch-user": "?1",
        "upgrade-insecure-requests": "1",
        "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36",
        "x-forwarded-proto": "https",
        "x-real-ip": "8.20.126.63"
      },
      "cf": {
        "longitude": "8.68430",
        "latitude": "50.11880",
        "tlsCipher": "AEAD-AES128-GCM-SHA256",
        "continent": "EU",
        "asn": 13335,
        "clientAcceptEncoding": "gzip, deflate, br",
        "country": "DE",
        "isEUCountry": "1",
        "tlsClientAuth": {
          "certIssuerDNLegacy": "",
          "certIssuerSKI": "",
          "certSubjectDNRFC2253": "",
          "certSubjectDNLegacy": "",
          "certFingerprintSHA256": "",
          "certNotBefore": "",
          "certSKI": "",
          "certSerial": "",
          "certIssuerDN": "",
          "certVerified": "NONE",
          "certNotAfter": "",
          "certSubjectDN": "",
          "certPresented": "0",
          "certRevoked": "0",
          "certIssuerSerial": "",
          "certIssuerDNRFC2253": "",
          "certFingerprintSHA1": ""
        },
        "tlsExportedAuthenticator": {
          "clientFinished": "b1e20a5740a343fcfb6467be09be00535855a9941070aa87dd4415f8b4ce4813",
          "clientHandshake": "fb3a0192daba988d584c9e608f7ee16806e3d8039bc0a7756062f179c8f5996f",
          "serverHandshake": "341a06efb07e5c69e892a125b2c9f398a7d9635e045aea84ed4fb1e83df12dd3",
          "serverFinished": "ab86bdcd773353e83d726a1e7e50a7bf38163fb5edaea1081ba6a895215bf0fb"
        },
        "tlsVersion": "TLSv1.3",
        "colo": "FRA",
        "timezone": "Europe/Berlin",
        "city": "Frankfurt am Main",
        "edgeRequestKeepAliveStatus": 1,
        "requestPriority": "",
        "httpProtocol": "HTTP/3",
        "region": "Hesse",
        "regionCode": "HE",
        "asOrganization": "Cloudflare Warp",
        "postalCode": "60313"
      }
    },
    "response": {
      "status": 200
    }
  },
  "id": 12

So it seems that it’s coming from a data centre not too far from me (I’m in France, data centre is Germany).

1 Like

Yep. Performance is going to be the focus over the open beta phase but it’s far too early to make any meaningful conclusions on latency or throughput.

Thanks very much for the info! I’m wondering if I can just use cloudlfare cache on top of this for the moment.

You could, and you definitely should. In the worker it’s pretty easy to implement, provided you have no issues with some files being cached and maybe changed on the source and not in the cache. At that point you’d need to implement some way to clear the cache or reduce the TTL.

Immutable files are ideal, as they are immutable.

This is strange, because I’m still seeing slow response times, even after adding caching. For example, this is a 40KB JS asset that should be sub-100ms to download, but I’m seeing times more like 300-800ms: https://bacon-cdn.andrewdjessop.workers.dev/index.ff866f9b.js

For comparison, the same asset hosted on Vercel is returning in <100ms every time.

Repo here if it’s interesting to anyone: https://github.com/andyjessop/bacon-cdn

Here’s the full code for the GET method in the worker:

if (request.method === 'GET') {
    const cacheUrl = new URL(request.url);

    // Construct the cache key from the cache URL
    const cacheKey = new Request(cacheUrl.toString(), request);
    const cache = caches.default;

    let response = await cache.match(cacheKey);

    if (!response) {
      console.log(
        `Response for request url: ${request.url} not present in cache. Fetching and caching request.`
      );
      // If not in cache, get it from origin
      const object = await BUCKET.get(objectKey);

      if (object === null) {
        return objectNotFound(objectKey);
      }

      const headers = new Headers();

      object.writeHttpMetadata(headers);
      headers.set('etag', object.httpEtag);
      const status = object.body ? 200 : 304;

      response = new Response(object.body, {
        headers,
        status
      });
      
      response.headers.append('Cache-Control', 's-maxage=31536000');

      event.waitUntil(cache.put(cacheKey, response.clone()));
    } else {
      console.log(`Cache hit for: ${request.url}.`);
    }

    return response;
  }

And here are the headers for the request/response in the browser:

Request URL: https://bacon-cdn.andrewdjessop.workers.dev/index.ff866f9b.js
Request Method: GET
Status Code: 200 
Remote Address: 172.67.190.48:443
Referrer Policy: strict-origin-when-cross-origin
alt-svc: h3=":443"; ma=86400, h3-29=":443"; ma=86400
cache-control: s-maxage=31536000
cf-ray: 7117266ab9b19b1b-FRA
content-encoding: br
content-type: text/javascript
date: Thu, 26 May 2022 14:20:11 GMT
etag: W/"4149c39b321c766141c7cc01263e592a"
expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
nel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=RfEC%2Fe%2FY7FuX0oi5gBubYyNqwJ24Bp6eKWe9%2F1hVoTZTM7FDyHXZDJMKgN05a5036CJKY7Mjl3iOACzg5b3KxRBENNRjGSYc5YjCFb4rSCzf6FB3l%2Fwt0wFhxCL9BhqsVckZFsOywvv%2FaNlMvNp53F7h30c9zg%3D%3D"}],"group":"cf-nel","max_age":604800}
server: cloudflare
vary: Accept-Encoding
:authority: bacon-cdn.andrewdjessop.workers.dev
:method: GET
:path: /index.ff866f9b.js
:scheme: https
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
accept-encoding: gzip, deflate, br
accept-language: en-GB,en-US;q=0.9,en;q=0.8
cache-control: max-age=0
if-none-match: W/"4149c39b321c766141c7cc01263e592a"
sec-fetch-dest: document
sec-fetch-mode: navigate
sec-fetch-site: none
sec-fetch-user: ?1
sec-gpc: 1
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36

The cache API needs a custom domain, not the default workers.dev domain.

3 Likes

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.