GithubHelp home page GithubHelp logo

Malformed cache data about cache-manager HOT 12 CLOSED

rivatove avatar rivatove commented on July 18, 2024 2
Malformed cache data

from cache-manager.

Comments (12)

francescosalvi avatar francescosalvi commented on July 18, 2024 2

I have a working repro, and the explanation for the behavior (and why it appears transient):

package.json (install latest version of cache-manager and cache-manager-ioredis-yet alongside the last former working versions, in my specific case 5.5.3/2.0.4)

{
  "name": "cache-manager-fiddle",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "cache-manager-prev": "npm:[email protected]",
    "cache-manager-ioredis-yet-prev": "npm:[email protected]",
    "cache-manager": "5.6.1",
    "cache-manager-ioredis-yet": "2.1.1"
  }
}

index.mjs

import { redisStore as createRedisStoreCurr } from 'cache-manager-ioredis-yet';
import { redisStore as createRedisStorePrev } from 'cache-manager-ioredis-yet-prev';
import { caching as createCacheCurr } from 'cache-manager';
import { caching as createCachePrev } from 'cache-manager-prev';

const connOptions = { host: 'localhost', port: 6379 };
const cachePrev = await createCachePrev(await createRedisStorePrev(connOptions));

const key = 'someKey';

await cachePrev.set(key, 'hello cache', 5000);
console.log(await cachePrev.get(key)); // outputs: hello cache

const cacheCurr = await createCacheCurr(await createRedisStoreCurr(connOptions));

console.log(await cacheCurr.get(key)); // outputs: { v: 'hello cache' }

process.exit(0);

Hence what we are abserving is a breaking change the internal expectations cache-manager-ioredis-yet makes about the schema it uses. In other words, all is fine if the cache store gets completely wiped an repopulated across package upgrade, but all data previously present becomes invalid.

from cache-manager.

Kauhsa avatar Kauhsa commented on July 18, 2024 1

I suspect #681 has something to do with this.

from cache-manager.

evenisse avatar evenisse commented on July 18, 2024 1

Yep. I think the guidance is to flush the cache when moving to this and that resolves it. Going to close this as there isnt a good way to handle it.

Hello,
It's not a solution. With 2 applications sharing the same cache data, if one application can't be updated, it's impossible to upgrade the other to the new version of cache-manager. Data storage must be independent of the library used.

from cache-manager.

jaredwray avatar jaredwray commented on July 18, 2024

@rivatove - we upgraded to telejson a while back which could store the objects that way but should not affect you retrieving the value. Can you give an example of how you see this happening? Do you have code example to reproduce?

from cache-manager.

rivatove avatar rivatove commented on July 18, 2024

@rivatove - we upgraded to telejson a while back which could store the objects that way but should not affect you retrieving the value. Can you give an example of how you see this happening? Do you have code example to reproduce?

Unfortunately I don't have more detailed examples as it happened in production.

Most important info:

  • rolling back to 5.5.3/2.0.4 solved the issue completely;
  • the issue was localized to the retrieval logic, it did not corrupt the data in the store. 2 versions could operate against the same store, and only the newest version would include this bug.
  • it did not occur 100% of the time. The version used to store the value in the cache could have played a part.

from cache-manager.

jaredwray avatar jaredwray commented on July 18, 2024

I have a working repro, and the explanation for the behavior (and why it appears transient):

package.json (install latest version of cache-manager and cache-manager-ioredis-yet alongside the last former working versions, in my specific case 5.5.3/2.0.4)

{
  "name": "cache-manager-fiddle",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "cache-manager-prev": "npm:[email protected]",
    "cache-manager-ioredis-yet-prev": "npm:[email protected]",
    "cache-manager": "5.6.1",
    "cache-manager-ioredis-yet": "2.1.1"
  }
}

index.mjs

import { redisStore as createRedisStoreCurr } from 'cache-manager-ioredis-yet';
import { redisStore as createRedisStorePrev } from 'cache-manager-ioredis-yet-prev';
import { caching as createCacheCurr } from 'cache-manager';
import { caching as createCachePrev } from 'cache-manager-prev';

const connOptions = { host: 'localhost', port: 6379 };
const cachePrev = await createCachePrev(await createRedisStorePrev(connOptions));

const key = 'someKey';

await cachePrev.set(key, 'hello cache', 5000);
console.log(await cachePrev.get(key)); // outputs: hello cache

const cacheCurr = await createCacheCurr(await createRedisStoreCurr(connOptions));

console.log(await cacheCurr.get(key)); // outputs: { v: 'hello cache' }

process.exit(0);

Hence what we are abserving is a breaking change the internal expectations cache-manager-ioredis-yet makes about the schema it uses. In other words, all is fine if the cache store gets completely wiped an repopulated across package upgrade, but all data previously present becomes invalid.

Thanks for doing this. Can you tell me what the schema looks like in previous as maybe we can just handle an conversion for it in the new code base?

from cache-manager.

francescosalvi avatar francescosalvi commented on July 18, 2024

@jaredwray no problem!

Can you tell me what the schema looks like in previous

If I do a "raw" get using ioredis right after the set using 5.5.3/2.1.1 in my snippet above, I get back {"v":"hello cache"}, so it appears that the wrapping of the user/business payload with the v field was already in place in the previous version.

This is confirmed If I diff the implementations ("prev" -> "curr"):
image

What's changed is that previously stringify used to include the "wrapping" parts in redis, while in the new version they are stripped using slice.

I am thinking that even by implementing a heuristic that handles both the old and new format (like: "if it's an object with strictly one property named v, then it must come from version < xxx and we should unwrap it"), there probably isn't a way to 100% exclude false positives (users could have a v as part of their business payload ?).

from cache-manager.

jaredwray avatar jaredwray commented on July 18, 2024

Yep. I think the guidance is to flush the cache when moving to this and that resolves it. Going to close this as there isnt a good way to handle it.

from cache-manager.

rvitaliy avatar rvitaliy commented on July 18, 2024

agree with @evenisse

another problem what is see is that two different applications can use different approach/libraries to store and get values, the contract of what i put and what i get at the level of the storage MUST be respected!
Libraries like this that have the purpose to be a wrapper that allow easier change the storage driver or just hide implementation detail MUST not alterate input and output datas at the storage level.

i mean:

NodeJSApplication: cacheManager.put('ping', 'pong');

storage: [{'ping': {'v': 'pong'}}]  // expected result storage: [{'ping': 'pong'}]

PHPApplication: redisDirectImplementation.get('ping') > result {'v': 'pong'} // ERROR!
GolangApplication: redisDirectImplementation.get('ping') > result {'v': 'pong'} // ERROR!

// and viceversa

GolangApplication|PHPApplication.put('key1', 'value1');
NodeJSApplication.cacheManager.get('key1') > result 'value1' expected {'v': 'value1'}

from cache-manager.

rvitaliy avatar rvitaliy commented on July 18, 2024

@jaredwray Hi, given all these considerations, do you intend to investigate further to determine if we need to rollback?

IMHO: yes, we should.

from cache-manager.

jaredwray avatar jaredwray commented on July 18, 2024

@rvitaliy - I beleive it is not doing this anymore. What version are you on as what is the code example that does this?

from cache-manager.

evenisse avatar evenisse commented on July 18, 2024

My problem is different. It's not between 2 versions of cache-manager but between 2 applications.
One application uses a redis library directly (ioredis) and the other uses cache-manager.
As cache-manager only accepts object with v field, it isn't possible to set data in the first app with a redis lib and read then in the other app with cache-manager. And reciprocally.

import { caching as createCacheCurr } from 'cache-manager';
import Redis from 'ioredis';
const redis = new Redis();

const connOptions = { host: 'localhost', port: 6379 };
const cacheCurr = await createCacheCurr(await createRedisStoreCurr(connOptions));

const key = 'someKey';

await redis.set(key, 'hello cache', 'PX', 5000);
console.log('From Redis: ', await redis.get(key)); // outputs: hello cache
console.log('From Cache: ', await cacheCurr.get(key)); // error

process.exit(0);

The output is

undefined:1
{"v":hello cache}
     ^

SyntaxError: Unexpected token 'h', "{"v":hello cache}" is not valid JSON
    at JSON.parse (<anonymous>)
    at Object.parse (/home/evenisse/test_redis_cache_manager/node_modules/telejson/dist/index.js:1565:24)
    at parse (/home/evenisse/test_redis_cache_manager/node_modules/cache-manager-ioredis-yet/dist/index.js:33:35)
    at Object.get (/home/evenisse/test_redis_cache_manager/node_modules/cache-manager-ioredis-yet/dist/index.js:61:24)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async file:///home/evenisse/test_redis_cache_manager/index.mjs:13:29

Why do you use an object with a v field and not directly th real value?

from cache-manager.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.