Skip to content
This repository has been archived by the owner on Oct 27, 2020. It is now read-only.

feat: add support for custom cache stores (options.read/options.write) #19

Merged
merged 1 commit into from
Nov 15, 2017

Conversation

Mordred
Copy link
Contributor

@Mordred Mordred commented Sep 27, 2017

Hi, I want to change how cache data are stored in my project. In my case I want to use Redis server like this:

const redis = require('redis');

// ...
// ... connect to client
// ...

const BUILD_CACHE_TIMEOUT = 24 * 3600; // 1 day

function redisKey(filename) {
    return `build:cache:${filename}`;
}

function readCache(filename, callback) {
      client.get(redisKey(filename), (err, result) => {
          if (err) {
              return callback(err);
          }

          if (!result) {
              return callback(new Error(`Key ${redisKey(filename)} not found`));
          }

          callback(null, result);
    });
}

function writeCache(filename, content, callback) {
      client.set(redisKey(filename), content, 'EX', BUILD_CACHE_TIMEOUT, callback);
}

Then in my webpack config:

{
  loader: 'cache-loader',
  options: {
    readCache,
    writeCache,
  },
},

For that I needed writeCache and readCache hooks.

@jsf-clabot
Copy link

jsf-clabot commented Sep 27, 2017

CLA assistant check
All committers have signed the CLA.

Copy link
Member

@alexander-akait alexander-akait left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thank you!

Copy link
Member

@michael-ciniawsky michael-ciniawsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would read/write from e.g a database be faster then fs ?
What 'real' use cases does this offer to users?

I'm not 💯 convinced if this is actually needed...

README.md Outdated
@@ -49,6 +49,8 @@ module.exports = {
|:--:|:--:|:-----:|:----------|
|**`cacheDirectory`**|`{String}`|`path.resolve('.cache-loader')`|Provide a cache directory where cache items should be stored|
|**`cacheIdentifier`**|`{String}`|`cache-loader:{version} {process.env.NODE_ENV}`|Provide an invalidation identifier which is used to generate the hashes. You can use it for extra dependencies of loaders.|
|**`writeCache`**|`{Function(cacheFilename, content, callback) -> {void}}`|`undefined`|Allows you to override default write cache data to file (e.g. Redis, memcached).|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just writer (options.writer) ?

README.md Outdated
@@ -49,6 +49,8 @@ module.exports = {
|:--:|:--:|:-----:|:----------|
|**`cacheDirectory`**|`{String}`|`path.resolve('.cache-loader')`|Provide a cache directory where cache items should be stored|
|**`cacheIdentifier`**|`{String}`|`cache-loader:{version} {process.env.NODE_ENV}`|Provide an invalidation identifier which is used to generate the hashes. You can use it for extra dependencies of loaders.|
|**`writeCache`**|`{Function(cacheFilename, content, callback) -> {void}}`|`undefined`|Allows you to override default write cache data to file (e.g. Redis, memcached).|
|**`readCache`**|`{Function(cacheFilename, callback) -> {void}}`|`undefined`|Allows you to override default read cache data from file.|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just reader (options.reader) ?

README.md Outdated
@@ -49,6 +49,8 @@ module.exports = {
|:--:|:--:|:-----:|:----------|
|**`cacheDirectory`**|`{String}`|`path.resolve('.cache-loader')`|Provide a cache directory where cache items should be stored|
|**`cacheIdentifier`**|`{String}`|`cache-loader:{version} {process.env.NODE_ENV}`|Provide an invalidation identifier which is used to generate the hashes. You can use it for extra dependencies of loaders.|
|**`writeCache`**|`{Function(cacheFilename, content, callback) -> {void}}`|`undefined`|Allows you to override default write cache data to file (e.g. Redis, memcached).|
|**`readCache`**|`{Function(cacheFilename, callback) -> {void}}`|`undefined`|Allows you to override default read cache data from file.|

<h2 align="center">Examples</h2>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add an example aswell

@Mordred
Copy link
Contributor Author

Mordred commented Sep 29, 2017

@michael-ciniawsky fs is faster than database, but we are building application for production in the docker. Saving cached data to filesystem is not an option because mounting volumes during docker build is not possible.

I have changed option names and added example from my first post.

README.md Outdated
@@ -95,6 +97,62 @@ module.exports = {
}
```

### `Write cache data to redis`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please try to make this example a bit more generic? e.g Database Intergation something like that :)

@michael-ciniawsky michael-ciniawsky changed the title feat: Allow to change how the cache is stored feat: add support for custom cache store mechanisms (options.reader/options.writer) Oct 19, 2017
Copy link
Member

@sokra sokra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • rename reader -> read, writer -> write
  • move the JSON.stringify and JSON.parse logic into the read and write methods.
  • rename cacheFilename to cacheKey
  • add a makeKey function makeKey(options, request)
  • default makeKey = path.join(..., digest(cacheIdentifer + request) + .json

@sokra
Copy link
Member

sokra commented Oct 19, 2017

cache-loader should no longer talk about filenames. Filenames should only occur in the default read/write/key implementation.

@michael-ciniawsky michael-ciniawsky changed the title feat: add support for custom cache store mechanisms (options.reader/options.writer) feat: add support for custom cache store mechanisms (options.read/options.write) Oct 19, 2017
@Mordred Mordred force-pushed the own-cache branch 2 times, most recently from 20d9c2d to 3afabf8 Compare October 23, 2017 08:55
@Mordred
Copy link
Contributor Author

Mordred commented Oct 23, 2017

@sokra refactored

Copy link
Member

@michael-ciniawsky michael-ciniawsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is options.makeKey mandatory functionality or additional ? It's better named options.cacheKey for naming consistency with options.cacheDirectory,options.cacheIdentfier

README.md Outdated
@@ -95,6 +98,82 @@ module.exports = {
}
```

### `Example of Database Integration`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

### Database Intergration

src/index.js Outdated
@@ -9,7 +9,18 @@ const pkgVersion = require('../package.json').version;
const defaultCacheDirectory = path.resolve('.cache-loader');
const ENV = process.env.NODE_ENV || 'development';

const defaultOptions = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defaultOptions => defaults

src/index.js Outdated
@@ -9,7 +9,18 @@ const pkgVersion = require('../package.json').version;
const defaultCacheDirectory = path.resolve('.cache-loader');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- const defaultCacheDirectory = path.resolve('.cache-loader');

src/index.js Outdated
@@ -9,7 +9,18 @@ const pkgVersion = require('../package.json').version;
const defaultCacheDirectory = path.resolve('.cache-loader');
const ENV = process.env.NODE_ENV || 'development';

const defaultOptions = {
cacheDirectory: defaultCacheDirectory,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cacheDirectory: path.resolve('.cache-loader'),

src/index.js Outdated
function loader(...args) {
const loaderOptions = loaderUtils.getOptions(this) || {};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- const loaderOptions = loaderUtils.getOptions(this) || {};

src/index.js Outdated
function write(cacheKey, data, callback) {
const dirname = path.dirname(cacheKey);
const content = JSON.stringify(data);
if (directoriesExists[dirname]) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (directories.has(dirname)) {

src/index.js Outdated
callback(mkdirErr);
return;
}
directoriesExists[dirname] = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

\ndirectories.add(dirname)

src/index.js Outdated
});
}

function makeKey(options, request) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

function cacheKey(options, request) {

src/index.js Outdated

function makeKey(options, request) {
const { cacheIdentifier, cacheDirectory } = options;
const hash = digest(`${cacheIdentifier}\n${request}`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

\n

src/index.js Outdated
const options = Object.assign({}, defaultOptions, loaderOptions);
const { cacheIdentifier, cacheDirectory } = options;
const { read: readFn, makeKey: makeKeyFn } = options;
const data = dataInput;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this to L67

@Crazymax11
Copy link

Hello
We also tried to use cache loader to build application in docker and met two problems:

  • Cache loader uses mtime to check if dependency changed, but git doesn't store files mtime, so after git clone we always have new mtime and no cache used.
  • It seems that cache loader doesn't use content of resource to check if resources changed, cache loader just checks if dependencies changed.

It would be nice to know if module changed by checking hash content instead of mtime. It would very nice to provide an api to set checkIfChanged function as option.

Can it be done in this PR?

@Mordred
Copy link
Contributor Author

Mordred commented Oct 24, 2017

@michael-ciniawsky makeKey -> cacheKey

@Crazymax11 We have different strategy. Instead of directly cloning repo in the docker image (which is slow), we have cloned repository on the build machine. During build we update this repo with git pull and copy it into docker context with COPY.

Copy link
Member

@michael-ciniawsky michael-ciniawsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final nits 🐦 and g2g :)

README.md Outdated
|**`cacheIdentifier`**|`{String}`|`cache-loader:{version} {process.env.NODE_ENV}`|Provide an invalidation identifier which is used to generate the hashes. You can use it for extra dependencies of loaders.|
|**`cacheDirectory`**|`{String}`|`path.resolve('.cache-loader')`|Provide a cache directory where cache items should be stored (used for default read/write implementation)|
|**`cacheIdentifier`**|`{String}`|`cache-loader:{version} {process.env.NODE_ENV}`|Provide an invalidation identifier which is used to generate the hashes. You can use it for extra dependencies of loaders. (used for default read/write implementation)|
|**`cacheKey`**|`{Function(options, request) -> {string}}`|`undefined`|Allows you to override default cache key generator.|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please finally move this to be the first item in the options table (before cacheDirectory) && -> {string} => -> {String}

src/index.js Outdated
};
const options = Object.assign({}, defaultOptions, loaderOptions);
const { cacheIdentifier, cacheDirectory } = options;
const options = Object.assign({}, defaults, loaderUtils.getOptions(this));
const data = dataInput;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- const data = dataInput

#19 (comment)

return;
}

directories.add(dirname);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

\n


function cacheKey(options, request) {
const { cacheIdentifier, cacheDirectory } = options;
const hash = digest(`${cacheIdentifier}\n${request}`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

\n

src/index.js Outdated
const hash = digest(`${cacheIdentifier}\n${remainingRequest}`);
const cacheFile = path.join(cacheDirectory, `${hash}.json`);
const { read: readFn, cacheKey: cacheKeyFn } = options;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const data = dataInput

data.cacheKey = cacheKeyFn(options, remainingRequest)
data.remainingRequest = remainingRequest

@Mordred
Copy link
Contributor Author

Mordred commented Oct 24, 2017

@michael-ciniawsky Changed.

Can I ask you why are these \n changes so important? Current index.js has no newlines in function definitions and I tried to match that style :)

@michael-ciniawsky
Copy link
Member

Can I ask you why are these \n changes so important?

Of course, Code Readability. I know it is annoying and doesn't add anything, but that code needs to be maintained and whitespace in particular is one of the cheapest ways to improve readability.

Current index.js has no newlines in function definitions and I tried to match that style :)

Yep but it definitely should have and if no one is pesky about it the tendency is to become one big 'BLOB' again. JSDoc is also missing 😛

@michael-ciniawsky
Copy link
Member

Anything left TODO left here ? @sokra @webpack-contrib/org-maintainers please review when time :)

@joshwiens
Copy link
Member

Looks like @Mordred has completed all the review comments. I don't personally see any other issues that stand out.

Unless Tobias has an objection, I'd say let it rip. :)

@hulkish
Copy link

hulkish commented Oct 28, 2017

LGTM. Although... I know this applies more to this project as a whole - would be nice to see some tests.

Not really practical to hold this up for that reason, though.

@joshwiens
Copy link
Member

@hulkish - @michael-ciniawsky setup a base test configuration & we are working through the repos that don't already have suites. Just haven't gotten here yet.

Copy link
Member

@sokra sokra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@michael-ciniawsky michael-ciniawsky changed the title feat: add support for custom cache store mechanisms (options.read/options.write) feat: add support for custom cache stores (options.read/options.write) Nov 15, 2017
@michael-ciniawsky michael-ciniawsky merged commit 060796b into webpack-contrib:master Nov 15, 2017
@michael-ciniawsky michael-ciniawsky removed this from the 1.3.0 milestone Feb 26, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants