154 Matching Annotations
  1. Dec 2023
    1. Rebuild your images regularly

      If you want both the benefits of caching, and to get security updates within a reasonable amount of time, you will need two build processes:

      1. The normal image build process that happens whenever you release new code.
      2. Once a week, or every night, rebuild your Docker image from scratch using docker build --pull --no-cache to ensure you have security updates.
    2. Disabling caching

      That suggests that sometimes you’re going to want to bypass the caching. You can do so by passing two arguments to docker build:

      • --pull: This pulls the latest version of the base Docker image, instead of using the locally cached one.
      • --no-cache: This ensures all additional layers in the Dockerfile get rebuilt from scratch, instead of relying on the layer cache.

      If you add those arguments to docker build you will be ensured that the new image has the latest (system-level) packages and security updates.

    3. As long as you’re relying on caching, you’ll still get the old, insecure packages distributed in your images
    4. caching means no updates
    5. caching can lead to insecure images.
  2. Nov 2023
    1. All about Caching: Strategies, Challenges and Optimization

      Caching is a crucial technique in modern #webservices, helping to improve #performance, reduce #latency, and minimize the load on servers and networks. The principle is simple: storing copies of data in a more quickly accessible location to enable faster access to that data. But, as with many technologies, the devil is in the details. In fact, creating a good configuration is very difficult and the smallest mistake can be very detrimental to the performance of our #website.

      caching

  3. Sep 2023
  4. Aug 2023
  5. Jul 2023
  6. Jun 2023
    1. ```js 'use strict';

      // --------------------------------------------------------------------

      class Cache extends Map { constructor(key) { super() this._key = key this.load() }

      set(k, v) { if (!this.has(k) || v !== this.get(k)) { super.set(k, v) this.save() } }

      delete(k) { if (super.has(k)) { super.delete(k) this.save() } }

      clear() { if (super.size() > 0) { super.clear() this.save() } }

      json() { let obj = Object.create(null)

      for (let k of this.keys()) {
        obj[k] = this.get(k)
      }
      
      return obj
      

      }

      save() { const data = this.json() this._save(this._key, data) this.dump({ data, event: 'SAVE' }) }

      load(s) { const data = this._load(this._key)

      if (typeof data !== 'object') {
        return
      }
      
      super.clear()
      
      for (let k of Object.keys(data)) {
        super.set(k, data[k])
      }
      
      this.dump({ data, event: 'LOAD' })
      

      }

      _save(key, data) {} _load(key) {}

      edit() { let res = window.prompt('Edit cached package URLs', JSON.stringify(this.json(), null, 2))

      if (res !== null) {
        try {
          const data = JSON.parse(res)
      
          super.clear()
      
          for (let k of Object.keys(data)) {
            super.set(k, data[k])
          }
      
          this.save()
        }
        catch (ex) {
          console.warn('Failed to update cache data: %s %o', ex.toString(), ex)
        }
      }
      

      }

      toString() { return ${this.constructor.name}<${this._key}>: keys=[ ${this.keys().sort().join(', ')} ] }

      dump({ data, event }) { console.group(${this.constructor.name}<${this._key}>: ${event || 'STATE'}:) console.info(JSON.stringify(data || this.json(), null, 2)) console.groupEnd() } }

      // --------------------------------------------------------------------

      class GMCache extends Cache { _save(key, data) { GM_setValue(key, JSON.stringify(data || {})) }

      _load(key) { return JSON.parse(GM_getValue(key) || '{}') } }

      // --------------------------------------------------------------------

      class StorageCache extends Cache { constructor(key, session) { super(key)

      this.storage = session ? window.sessionStorage : window.localStorage
      this.load()
      

      }

      _save(key, data) { this.storage.setItem(key, JSON.stringify(data || {})) }

      _load(key) { if (this.storage) { return JSON.parse(this.storage.getItem(key) || '{}') } } }

      // --------------------------------------------------------------------

      let c = new StorageCache('test-data') // c.set('jshint-summary', { name: 'jshint-summary', homepage: 'https://github.com/spiralx/jshint-summary' }) ```

  7. May 2023
    1. Figured it out. Cache-Control header is required.

      js const headers = { 'Cache-Control': 'public, max-age=604800' }; const request = new Request('https://foobar.com/') const cacheResponse = new Response('bar',{ headers }) const cache = caches.default await cache.put(request, cacheResponse) const response = await cache.match(request);

  8. Mar 2023
    1. <table><tbody><tr><th colspan="4" rowspan="1">Status</th><th colspan="4" rowspan="1">Description</th></tr><tr><td colspan="5" rowspan="1">HIT</td><td colspan="5" rowspan="1">The resource was found in Cloudflare’s cache.</td></tr><tr><td colspan="5" rowspan="1">MISS</td><td colspan="5" rowspan="1">The resource was not found in Cloudflare’s cache and was served from the origin web server.</td></tr><tr><td colspan="5" rowspan="1">NONE/UNKNOWN</td><td colspan="5" rowspan="1">Cloudflare generated a response that denotes the asset is not eligible for caching. This may have happened because:
    2. A Worker generated a response without sending any subrequests. In this case, the response did not come from cache, so the cache status will be none/unknown.
    3. A Worker request made a subrequest (fetch). In this case, the subrequest will be logged with a cache status, while the main request will be logged with none/unknown status (the main request did not hit cache, since Workers sits in front of cache).
    4. A Firewall rule was triggered to block a request. The response will come from the edge network before it hits cache. Since there is no cache status, Cloudflare will log as none/unknown.
    5. A redirect page rule caused the edge network to respond with a redirect to another asset/URL. This redirect response happens before the request reaches cache, so the cache status is none/unknown.
    6. </td></tr><tr><td colspan="5" rowspan="1">EXPIRED</td><td colspan="5" rowspan="1">The resource was found in Cloudflare’s cache but was expired and served from the origin web server.</td></tr><tr><td colspan="5" rowspan="1">STALE</td><td colspan="5" rowspan="1">The resource was served from Cloudflare’s cache but was expired. Cloudflare could not contact the origin to retrieve an updated resource.</td></tr><tr><td colspan="5" rowspan="1">BYPASS</td><td colspan="5" rowspan="1">The origin server instructed Cloudflare to bypass cache via a Cache-Control header set to no-cache,private, or max-age=0 even though Cloudflare originally preferred to cache the asset. BYPASS is returned when enabling Origin Cache-Control. Cloudflare also sets BYPASS when your origin web server sends cookies in the response header.</td></tr><tr><td colspan="5" rowspan="1">REVALIDATED</td><td colspan="5" rowspan="1">The resource is served from Cloudflare’s cache but is stale. The resource was revalidated by either an If-Modified-Since header or an If-None-Match header.</td></tr><tr><td colspan="5" rowspan="1">UPDATING</td><td colspan="5" rowspan="1">The resource was served from Cloudflare’s cache and was expired, but the origin web server is updating the resource. UPDATING is typically only seen for very popular cached resources.</td></tr><tr><td colspan="5" rowspan="1">DYNAMIC</td><td colspan="5" rowspan="1">Cloudflare does not consider the asset eligible to cache and your Cloudflare settings do not explicitly instruct Cloudflare to cache the asset. Instead, the asset was requested from the origin web server. Use Page Rules to implement custom caching options.</td></tr></tbody></table>
    1. Send the 304 Not Modified response

      ```js import etag from "etag"; import { renderToString } from "react-dom/server"; import type { EntryContext, HandleDataRequestFunction } from "remix"; import { RemixServer } from "remix";

      export default function handleRequest( request: Request, status: number, headers: Headers, remixContext: EntryContext ) { let markup = renderToString( <RemixServer context={remixContext} url={request.url} /> );

      headers.set("Content-Type", "text/html"); headers.set("ETag", etag(markup));

      // check if the If-None-Match header matches the ETag if (request.headers.get("If-None-Match") === headers.get("ETag")) { // and send an empty Response with status 304 and the headers. return new Response("", { status: 304, headers }); }

      return new Response("<!DOCTYPE html>" + markup, { status, headers }); }

      export let handleDataRequest: HandleDataRequestFunction = async ( response: Response, { request } ) => { let body = await response.text();

      if (request.method.toLowerCase() === "get") { response.headers.set("etag", etag(body)); // As with document requests, check the If-None-Match header // and compare it with the Etag, if they match, send the empty 304 Response if (request.headers.get("If-None-Match") === response.headers.get("ETag")) { return new Response("", { status: 304, headers: response.headers }); } }

      return response; }; ```

    2. All Together

      ```js import etag from "etag"; import { renderToString } from "react-dom/server"; import type { EntryContext, HandleDataRequestFunction } from "remix"; import { RemixServer } from "remix";

      export default function handleRequest( request: Request, status: number, headers: Headers, remixContext: EntryContext ) { let markup = renderToString( <RemixServer context={remixContext} url={request.url} /> );

      headers.set("Content-Type", "text/html"); headers.set("ETag", etag(markup));

      return new Response("<!DOCTYPE html>" + markup, { status, headers }); }

      export let handleDataRequest: HandleDataRequestFunction = async ( response: Response ) => { let body = await response.text(); response.headers.set("etag", etag(body)); return response; }; ```

    3. Using ETags for document requests

      ```js import etag from "etag"; import { renderToString } from "react-dom/server"; import type { EntryContext } from "remix"; import { RemixServer } from "remix";

      export default function handleRequest( request: Request, status: number, headers: Headers, remixContext: EntryContext ) { let markup = renderToString( <RemixServer context={remixContext} url={request.url} /> );

      headers.set("Content-Type", "text/html"); // add the Etag header using the markup as value headers.set("ETag", etag(markup));

      return new Response("<!DOCTYPE html>" + markup, { status, headers }); } ```

    4. Using ETags for data requests

      ```js import etag from "etag"; import type { HandleDataRequestFunction } from "remix";

      export let handleDataRequest: HandleDataRequestFunction = async ( response: Response, { request } ) => { let body = await response.text(); // parse the response body as text

      // only add the ETag for GET requests if (request.method.toLowerCase() === "get") { response.headers.set("etag", etag(body)); // and use it to create the ETag }

      return response; // return the response }; ```

    1. ```js import { renderToString } from "react-dom/server"; import { RemixServer } from "remix"; import type { EntryContext } from "remix"; import { etag } from 'remix-etag';

      export default function handleRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, remixContext: EntryContext ) { const markup = renderToString( <RemixServer context={remixContext} url={request.url} /> );

      responseHeaders.set("Content-Type", "text/html");

      const response = new Response("<!DOCTYPE html>" + markup, { status: responseStatusCode, headers: responseHeaders, }); return etag({ request, response }); } ```

    1. You'll notice that for the app/routes/jokes/$jokeId.tsx route in addition to Cache-Control we've also set Vary header to Cookie. This is because we're returning something that's specific to the user who is logged in. So we want the cache to associated to that particular Cookie value and not shared with different users, so the browser and CDN will not deliver the cached value if the cookie is different from the cached response's cookie.
  • Feb 2023
  • Jan 2023
  • Dec 2022
  • Aug 2022
    1. Using git remote set-head has the advantage of updating a cached answer, which you can then use for some set period. A direct query with git ls-remote has the advantage of getting a fresh answer and being fairly robust. The git remote show method seems like a compromise between these two that has most of their disadvantages with few of their advantages, so that's the one I would avoid.)
  • Jul 2022
  • Apr 2022
    1. Cache using fetch

      Determine how to cache a resource by setting TTLs, custom cache keys, and cache headers in a fetch request.

      ```js async function handleRequest(request) { const url = new URL(request.url);

      // Only use the path for the cache key, removing query strings // and always store using HTTPS, for example, https://www.example.com/file-uri-here const someCustomKey = https://${url.hostname}${url.pathname};

      let response = await fetch(request, { cf: { // Always cache this fetch regardless of content type // for a max of 5 seconds before revalidating the resource cacheTtl: 5, cacheEverything: true, //Enterprise only feature, see Cache API for other plans cacheKey: someCustomKey, }, }); // Reconstruct the Response object to make its headers mutable. response = new Response(response.body, response);

      // Set cache control headers to cache on browser for 25 minutes response.headers.set('Cache-Control', 'max-age=1500'); return response; }

      addEventListener('fetch', event => { return event.respondWith(handleRequest(event.request)); }); ```


      Caching HTML resources

      Setting the cache level to Cache Everything will override the default cacheability of the asset. For time-to-live (TTL), Cloudflare will still rely on headers set by the origin.

      js // Force Cloudflare to cache an asset fetch(event.request, { cf: { cacheEverything: true } });

  • Mar 2022
  • Feb 2022
  • Jan 2022
  • Dec 2021
    1. Currently starting to read https://github.com/substack/stream-handbook, maybe I just need a deeper understanding of streams

      According to @xander76 on Twitter the code would have to use a Transform stream, which looks something like this:

      let cacheEntry = "";
      renderToNodeStream(<Frontend/>)
        .pipe(new Transform({
          transform(chunk, enc, callback) {
            cacheEntry += chunk; callback(chunk);
          },
          flush(callback) {
            redis.set(req.path, cacheEntry);
          }
        })
        .pipe(res);
      
  • Jul 2021
  • Jun 2021
  • Feb 2021
    1. Space: Suppose we had infinite memory, then cache all the data; but we don't so we have to decide what to cache that is meaningful to have the cache implemented (is a ??K cache size enough for your use case? Should you add more?) - It's the balance with the resources available.
    2. Time: Suppose all your data was immutable, then cache all the data indefinitely. But this isn't always to case so you have to figure out what works for the given scenario (A person's mailing address doesn't change often, but their GPS position does).
    3. You have to guess when the data is not likely to be needed in memory. It can't be too short that the cache is useless, and too long that you'll get a memory leak.
    1. There’s only one hard thing in Computer Science: human communication. The most complex part of cache invalidation is figuring out what the heck people mean with the word cache. Once you get that sorted out, the rest is not that complicated; the tools are out there, and they’re pretty good.
    2. Cache and caching are some of the most ambiguous words in the IT world, and that’s because we can have multiple types of caching working in parallel and affecting content independently.
  • Jan 2021
    1. v1 tabs tightly couple to their v1 Cache; v2 tabs tightly couple to their v2 Cache. This tight coupling makes them “application caches.” The app must be completely shut down (all tabs closed) in order to upgrade atomically to the newest cache of code.
  • Dec 2020
    1. I have a feeling that this functionality is scuppered by #415 - since my browser caches the page for 10 minutes, meaning that the page is never hit and thus the preload is never run, regardless of whether session has been changed or not.
    1. I'd instinctively associate a this.cache() inside preload with the preloaded data only -- meaning no matter what crazy thing I did inside preload, the returned data would be cached locally, where local means either the client or server.
    1. Say I have a separate API server that provides content for a Sapper route. I could just query that API in my route's preload function, but if the content changes rarely, I don't want to incur that cost for every page load and would much rather cache responses from the API on the Sapper side.
    2. // app/middleware/cache.js,`use`ed in app/server.js import LRU from 'lru-cache'; const _cache = new LRU({ max: 500, maxAge: 1000 * 60 * 60 }); async function cache(req, res, next) { req.cache = _cache; next(); }
    1. Popular architectures deal with heavy traffic loads by adding logic to cache popular views and resources.