Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async Cache #33

Closed
tanishqmanuja opened this issue Dec 23, 2024 · 4 comments
Closed

Async Cache #33

tanishqmanuja opened this issue Dec 23, 2024 · 4 comments
Labels
enhancement New feature or request

Comments

@tanishqmanuja
Copy link

Can someone help me on using a async defaultCache for cachePlugin. I want to do filesystem IO for the cache so async is required.

@suhaotian
Copy link
Owner

What's the use cases of AsyncCache plugin?

@tanishqmanuja
Copy link
Author

tanishqmanuja commented Dec 24, 2024

I am using octokit to get stuff from github and want cache all get requests and persist the changes during server restarts (Astro Framework). So the filesystem cache should work but I don't want to use fs modules without async because that would block the page render.

Axios supports the same via interceptors so i thought there would be some support in xior also.

@suhaotian suhaotian added the enhancement New feature or request label Dec 25, 2024
@suhaotian suhaotian reopened this Jan 18, 2025
@suhaotian
Copy link
Owner

To persist cache data, update xior to latest version ^0.6.3, and implement the persistCachePlugin function:

Note: The following code assumes it only runs on the server side. If you are using Next.js or Remix, please separate fs operations to the server.

import { lru, LRU } from 'tiny-lru';
import { Xior as xior, delay, Xior } from 'xior';
import xiorCachePlugin from 'xior/plugins/cache';
import fs from 'fs/promises';

export function persistCachePlugin(
  xiorInstance: Xior,
  cache: LRU<any>,
  filePath: string,
  debounceTime = 500
) {
  xiorInstance.interceptors.response.use((response) => {
    if (!response.fromCache && response.cacheKey) {
      debouncedSyncToFs();
    }
    return response;
  });

  async function initFromFs() {
    try {
      const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
      data.forEach(([key, value]: [string, any]) => {
        if (value) {
          cache.set(key, Promise.resolve(value));
        }
      });
    } catch (e) {
      console.error('initFromFs error:', e);
    }
  }

  async function syncToFs() {
    const keys = cache.keys();
    const data = await Promise.all(keys.map(async (key) => [key, await cache.get(key)]));
    await fs.writeFile(filePath, JSON.stringify(data, null, 2));
  }
  const debouncedSyncToFs = debounce(syncToFs, debounceTime);

  function debounce(func: () => Promise<void>, wait: number) {
    let timeout: NodeJS.Timeout | null = null;
    return () => {
      if (timeout) {
        clearTimeout(timeout);
      }
      timeout = setTimeout(() => {
        func();
      }, wait);
    };
  }

  return { initFromFs };
}

const http = xior.create();
const cache = lru(100, 1000 * 60 * 5);
http.plugins.use(
  xiorCachePlugin({
    enableCache: (config) => config?.method === 'GET' || config?.isGet === true,
    defaultCache: cache,
  })
);

// You need to run `initFromFs` to initialize cache data from the file before sending any requests.
const { initFromFs } = persistCachePlugin(http, cache, './cache.json', 500);

You can also check the persistent cache test file: https://github.com/suhaotian/xior/blob/main/tests/src/tests/plugins/persist-cache.test.ts

@suhaotian suhaotian pinned this issue Jan 19, 2025
@tanishqmanuja
Copy link
Author

Thanks 💯

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants