Skip to content

Commit 4f43d85

Browse files
committed
Adding new fields for error, errorTime, and consecutiveErrors to records and stored data
1 parent 001db67 commit 4f43d85

File tree

8 files changed

+521
-210
lines changed

8 files changed

+521
-210
lines changed

lib/cacheism.js

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,20 @@ function _sanitize(string) {
77
function Cacheism(store) {
88
this.store = store;
99
this.status = common.Status;
10+
this.type = common.Type;
1011
}
1112

1213
Cacheism.prototype.go = async function (cacheDomain, cachePath, status, callback) {
1314
let response, name = this.cacheName(cacheDomain, cachePath);
1415

15-
try {
16+
let existing = new common.Miss(name, new Error('Missing cache'), 0);
17+
let hasCache = await this.store.isset(name);
1618

17-
let existing = new common.Miss(name, new Error('Missing cache'));
18-
let hasCache = await this.store.isset(name);
19+
if (hasCache) {
20+
existing = await this.store.get(name);
21+
}
1922

20-
if (hasCache) {
21-
existing = await this.store.get(name);
22-
}
23+
try {
2324

2425
if (status >= this.status.preferCache && hasCache) {
2526
response = existing;
@@ -30,20 +31,23 @@ Cacheism.prototype.go = async function (cacheDomain, cachePath, status, callback
3031
if (!(response instanceof common.Hit)) {
3132
response = new common.Hit(name, response);
3233
}
33-
await this.store.set(response);
3434
}
3535

3636
} catch (err) {
3737

38-
if (status >= this.status.cacheOnFail && await this.store.isset(name)) {
39-
response = await this.store.get(name);
38+
if (status >= this.status.cacheOnFail && hasCache) {
39+
response = existing;
4040
response.error = err;
41+
response.errorTime = new Date();
42+
response.consecutiveErrors++;
4143
} else {
42-
response = new common.Miss(name, err);
44+
response = new common.Miss(name, err, existing.consecutiveErrors + 1);
4345
}
4446

4547
}
4648

49+
await this.store.set(response);
50+
4751
Object.freeze(response);
4852
return response;
4953
}
@@ -68,6 +72,7 @@ Cacheism.Hit = common.Hit;
6872
Cacheism.Miss = common.Miss;
6973
Cacheism.Data = common.Data;
7074
Cacheism.Status = common.Status;
75+
Cacheism.Type = common.Type;
7176

7277
Cacheism.store = {
7378
filesystem: require('./store-filesystem'),

lib/common.js

Lines changed: 79 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,75 +7,140 @@ Status.cacheOnFail = 1;
77
Status.preferCache = 2;
88
Status.onlyCache = 3;
99

10+
class Type {}
11+
12+
Type.hit = 'Hit';
13+
Type.miss = 'Miss';
14+
1015
Object.freeze(Status);
1116

1217
class Hit {
1318
constructor(name, data, etag) {
14-
this.version = 2;
19+
this.version = 3;
1520
this.cacheName = name;
1621
this.cached = false;
1722
this.created = new Date();
1823
this.data = data;
1924
this.error = null;
25+
this.errorTime = null;
26+
this.consecutiveErrors = 0;
2027
this.etag = null == etag ? generateEtag(JSON.stringify(data)) : etag;
2128
this.isHit = true;
2229
this.isMiss = false;
2330
}
2431
}
2532

2633
class Miss {
27-
constructor(name, error) {
28-
this.version = 2;
34+
constructor(name, error, consecutiveErrors) {
35+
this.version = 3;
2936
this.cacheName = name;
3037
this.cached = false;
3138
this.created = new Date();
3239
this.data = null;
3340
this.error = error;
41+
this.errorTime = new Date(this.created);
42+
this.consecutiveErrors = consecutiveErrors;
3443
this.etag = null;
3544
this.isHit = false;
3645
this.isMiss = true;
3746
}
3847
}
3948

4049
class Data {
41-
constructor(version, name, created, data, etag) {
50+
constructor(version, type, name, created, data, error, errorTime, consecutiveErrors, etag) {
4251
this.version = version;
52+
this.type = type;
4353
this.cacheName = name;
4454
this.created = created;
4555
this.data = data;
56+
this.error = error;
57+
this.errorTime = errorTime;
58+
this.consecutiveErrors = consecutiveErrors;
4659
this.etag = etag;
4760
}
4861

49-
hit() {
50-
if (2 !== this.version) {
62+
response() {
63+
if (3 !== this.version) {
5164
throw new Error(`Unknown cache version number: ${this.version}`);
5265
}
5366

54-
const hit = new Hit(this.cacheName, this.data, this.etag);
55-
hit.cached = true;
56-
hit.created = this.created;
67+
let response;
68+
69+
if (this.type === Type.hit) {
70+
response = new Hit(this.cacheName, this.data, this.etag);
71+
response.cached = true;
72+
response.created = this.created;
73+
} else {
74+
response = new Miss(this.cacheName, this.error, this.consecutiveErrors);
75+
response.cached = true;
76+
response.created = this.created;
77+
response.errorTime = this.errorTime;
78+
}
5779

58-
return hit;
80+
return response;
5981
}
6082

6183
stringify() {
6284
return JSON.stringify({
6385
version: this.version,
86+
type: this.type,
6487
cacheName: this.cacheName,
6588
created: this.created,
6689
data: this.data,
90+
error: null == this.error ? null : this.error.toString(),
91+
errorTime: this.errorTime,
92+
consecutiveErrors: this.consecutiveErrors,
6793
etag: this.etag,
6894
}, null, 2);
6995
}
7096

71-
static fromHit(hit) {
72-
return new Data(hit.version, hit.cacheName, hit.created, hit.data, hit.etag);
97+
static fromResponse(response) {
98+
return new Data(
99+
response.version,
100+
response.isHit ? Type.hit : Type.miss,
101+
response.cacheName,
102+
response.created,
103+
response.data,
104+
response.error,
105+
response.errorTime,
106+
response.consecutiveErrors,
107+
response.etag
108+
);
73109
}
74110

75111
static parse(value) {
76112
const parsed = JSON.parse(value);
77-
return new Data(parsed.version, parsed.cacheName, new Date(parsed.created), parsed.data, parsed.etag);
113+
114+
if (2 === parsed.version) {
115+
return new Data(
116+
3,
117+
Type.hit,
118+
parsed.cacheName,
119+
new Date(parsed.created),
120+
parsed.data,
121+
null,
122+
null,
123+
0,
124+
parsed.etag
125+
);
126+
}
127+
128+
if (3 === parsed.version) {
129+
return new Data(
130+
3,
131+
parsed.type,
132+
parsed.cacheName,
133+
new Date(parsed.created),
134+
parsed.data,
135+
parsed.error,
136+
null === parsed.errorTime ? null : new Date(parsed.errorTime),
137+
parsed.consecutiveErrors,
138+
parsed.etag
139+
);
140+
}
141+
142+
throw new Error(`Unknown cache version number: ${parsed.version}`);
78143
}
79144
}
80145

81-
module.exports = { Hit, Miss, Data, Status };
146+
module.exports = { Hit, Miss, Data, Status, Type };

lib/store-filesystem.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ module.exports = function (config) {
1616
storeFilesystem.get = async (cacheName) => {
1717
const filename = path.resolve(config.datadir, `${cacheName}.json`);
1818
const data = await fsPromises.readFile(filename, 'utf8');
19-
return common.Data.parse(data).hit();
19+
return common.Data.parse(data).response();
2020
}
2121

2222
storeFilesystem.set = async (hit) => {
2323
const filename = path.resolve(config.datadir, `${hit.cacheName}.json`);
2424
_mkdir(path.dirname(filename));
25-
await fsPromises.writeFile(filename, common.Data.fromHit(hit).stringify(), 'utf8');
25+
await fsPromises.writeFile(filename, common.Data.fromResponse(hit).stringify(), 'utf8');
2626
}
2727

2828
storeFilesystem.isset = async (cacheName) => {

lib/store-memory.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ module.exports = function (config) {
44
const storeMemory = { data: {} };
55

66
storeMemory.get = async (cacheName) => {
7-
return common.Data.parse(storeMemory.data[cacheName]).hit();
7+
return common.Data.parse(storeMemory.data[cacheName]).response();
88
}
99

1010
storeMemory.set = async (hit) => {
11-
storeMemory.data[hit.cacheName] = common.Data.fromHit(hit).stringify();
11+
storeMemory.data[hit.cacheName] = common.Data.fromResponse(hit).stringify();
1212
}
1313

1414
storeMemory.isset = async (cacheName) => {

package-lock.json

Lines changed: 16 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"homepage": "https://github.com/andrewshell/cacheism#readme",
2323
"devDependencies": {
2424
"expect.js": "^0.3.1",
25-
"mocha": "^9.2.2"
25+
"mocha": "^9.2.2",
26+
"mockdate": "^3.0.5"
2627
},
2728
"dependencies": {
2829
"etag": "^1.8.1"

test/helpers.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
const expect = require('expect.js');
2+
const mockdate = require('mockdate');
3+
4+
const Cacheism = require('../lib/cacheism');
5+
6+
function expectCacheHit(c, cached, data) {
7+
expect(c).to.be.a(Cacheism.Hit);
8+
expect(c).to.have.property('version', 3);
9+
expect(c).to.have.property('cacheName', '-internal/cache');
10+
expect(c).to.have.property('cached', cached);
11+
expect(c).to.have.property('created');
12+
expect(c.created).to.be.a(Date);
13+
expect(c).to.have.property('data', data);
14+
expect(c).to.have.property('etag');
15+
}
16+
17+
function expectCacheMiss(c, cached, data) {
18+
expect(c).to.be.a(Cacheism.Miss);
19+
expect(c).to.have.property('version', 3);
20+
expect(c).to.have.property('cacheName', '-internal/cache');
21+
expect(c).to.have.property('cached', cached);
22+
expect(c).to.have.property('created');
23+
expect(c.created).to.be.a(Date);
24+
expect(c).to.have.property('data', data);
25+
expect(c).to.have.property('etag', null);
26+
}
27+
28+
function expectCacheNoErrors(c) {
29+
expect(c).to.have.property('error', null);
30+
expect(c).to.have.property('errorTime', null);
31+
expect(c).to.have.property('consecutiveErrors', 0);
32+
}
33+
34+
function expectCacheErrors(c, error, errors) {
35+
expect(c).to.have.property('error');
36+
expect(c.error).to.be.an(Error);
37+
expect(c.error).to.have.property('message', error);
38+
expect(c).to.have.property('errorTime');
39+
expect(c.errorTime).to.be.a(Date);
40+
expect(c).to.have.property('consecutiveErrors', errors);
41+
}
42+
43+
function expectDataHit(d, data, etag) {
44+
expect(d).to.be.a(Cacheism.Data);
45+
expect(d).to.have.property('version', 3);
46+
expect(d).to.have.property('type', Cacheism.Type.hit);
47+
expect(d).to.have.property('created');
48+
expect(d.created).to.be.a(Date);
49+
expect(d).to.have.property('data', data);
50+
expect(d).to.have.property('etag', etag);
51+
}
52+
53+
function expectDataMiss(d, data, etag) {
54+
expect(d).to.be.a(Cacheism.Data);
55+
expect(d).to.have.property('version', 3);
56+
expect(d).to.have.property('type', Cacheism.Type.miss);
57+
expect(d).to.have.property('created');
58+
expect(d.created).to.be.a(Date);
59+
expect(d).to.have.property('data', data);
60+
expect(d).to.have.property('etag', etag);
61+
}
62+
63+
function expectDataNoErrors(d) {
64+
expect(d).to.have.property('error', null);
65+
expect(d).to.have.property('errorTime', null);
66+
expect(d).to.have.property('consecutiveErrors', 0);
67+
}
68+
69+
70+
function expectDataErrors(d, error, errors) {
71+
expect(d).to.have.property('error', error);
72+
expect(d).to.have.property('errorTime');
73+
expect(d.errorTime).to.be.a(Date);
74+
expect(d).to.have.property('consecutiveErrors', errors);
75+
}
76+
77+
module.exports = {
78+
expectCacheHit,
79+
expectCacheMiss,
80+
expectCacheNoErrors,
81+
expectCacheErrors,
82+
expectDataHit,
83+
expectDataMiss,
84+
expectDataNoErrors,
85+
expectDataErrors,
86+
};

0 commit comments

Comments
 (0)