Skip to content

Commit aa1e493

Browse files
author
Aaron Chambers
committed
Merge pull request #24 from ghedamat/sorted-set
use a sorted set to store the list of revisions
2 parents e89d6f8 + 61bba36 commit aa1e493

File tree

3 files changed

+89
-44
lines changed

3 files changed

+89
-44
lines changed

lib/redis.js

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,9 @@ module.exports = CoreObject.extend({
6262
},
6363

6464
fetchRevisions: function(keyPrefix) {
65-
var currentKey = keyPrefix + ':current';
66-
6765
return Promise.hash({
6866
revisions: this._listRevisions(keyPrefix),
69-
current: this._client.get(currentKey)
67+
current: this._activeRevision(keyPrefix)
7068
}).then(function(results) {
7169
return results.revisions.map(function(revision) {
7270
return {
@@ -79,7 +77,7 @@ module.exports = CoreObject.extend({
7977

8078
_listRevisions: function(keyPrefix) {
8179
var client = this._client;
82-
return client.lrange(keyPrefix, 0, this._maxNumberOfRecentUploads - 1);
80+
return client.zrevrange(keyPrefix, 0, -1);
8381
},
8482

8583
_validateRevisionKey: function(revisionKey, revisions) {
@@ -106,16 +104,38 @@ module.exports = CoreObject.extend({
106104
})
107105
.then(function() {
108106
return client.set(redisKey, value);
109-
})
107+
});
110108
},
111109

112110
_updateRecentUploadsList: function(keyPrefix, revisionKey) {
113111
var client = this._client;
114-
return client.lpush(keyPrefix, revisionKey);
112+
var score = new Date().getTime();
113+
return client.zadd(keyPrefix, score, revisionKey);
114+
},
115+
116+
_activeRevision: function(keyPrefix) {
117+
var currentKey = keyPrefix + ':current';
118+
return this._client.get(currentKey);
115119
},
116120

117121
_trimRecentUploadsList: function(keyPrefix, maxEntries) {
118122
var client = this._client;
119-
return client.ltrim(keyPrefix, 0, maxEntries - 1);
123+
124+
return Promise.hash({
125+
revisionsToBeRemoved: client.zrange(keyPrefix, 0, -(maxEntries + 1)),
126+
current: this._activeRevision(keyPrefix)
127+
}).then(function(results) {
128+
var revisions = results.revisionsToBeRemoved;
129+
var current = results.current;
130+
if (!revisions) {
131+
return;
132+
}
133+
revisions.forEach(function(revision) {
134+
if (revision !== current) {
135+
client.del(keyPrefix + ":" + revision);
136+
client.zrem(keyPrefix, revision);
137+
}
138+
});
139+
});
120140
}
121141
});

tests/helpers/fake-redis-client.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,24 @@ var CoreObject = require('core-object');
22

33
module.exports = CoreObject.extend({
44
init: function (options) {
5+
this.recentRevisions = [];
56
this.options = options;
67
},
78
get: function(key) {
89
return Promise.resolve('some-other-value');
910
},
1011
set: function() { },
11-
lpush: function() { },
12-
ltrim: function() { }
12+
del: function() { },
13+
zadd: function(key, score , tag) {
14+
this.recentRevisions.push(key + ':' + tag);
15+
},
16+
zrem: function(val,revision) {
17+
var i = this.recentRevisions.indexOf(revision)
18+
this.recentRevisions.splice(i,1);
19+
},
20+
zrange: function() {
21+
},
22+
zrevrange: function() {
23+
return this.recentRevisions;
24+
}
1325
});

tests/unit/lib/redis-nodetest.js

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -61,44 +61,67 @@ describe('redis', function() {
6161
});
6262

6363
it('updates the list of recent uploads once upload is successful', function() {
64-
var recentUploads = [];
64+
var redis = new Redis({}, new FakeRedis(FakeClient.extend({
65+
get: function(key) {
66+
return Promise.resolve(null);
67+
}
68+
})));
69+
70+
var promise = redis.upload('key', 'value');
71+
return assert.isFulfilled(promise)
72+
.then(function() {
73+
assert.equal(redis._client.recentRevisions.length, 1);
74+
assert.equal(redis._client.recentRevisions[0], 'key:default');
75+
});
76+
});
77+
78+
it('trims the list of recent uploads and removes the index key', function() {
79+
var finalUploads = ['3','4','5','6','7','8','9','10','11','key:12'];
6580

6681
var redis = new Redis({}, new FakeRedis(FakeClient.extend({
6782
get: function(key) {
6883
return Promise.resolve(null);
6984
},
70-
lpush: function(key, tag) {
71-
recentUploads.push(key + tag);
85+
del: function(key) {
86+
assert(key === 'key:1' || key === 'key:2');
7287
},
88+
zrange: function() {
89+
return this.recentRevisions.slice(0,2);
90+
}
7391
})));
7492

75-
var promise = redis.upload('key', 'value');
93+
redis._client.recentRevisions = ['1','2','3','4','5','6','7','8','9','10','11'];
94+
95+
var promise = redis.upload('key', '12', 'value');
7696
return assert.isFulfilled(promise)
7797
.then(function() {
78-
assert.equal(recentUploads.length, 1);
79-
assert.equal(recentUploads[0], 'keydefault');
98+
assert.equal(redis._client.recentRevisions.length, 10);
99+
assert.deepEqual(redis._client.recentRevisions, finalUploads);
80100
});
81101
});
82102

83-
it('trims the list of recent uploads', function() {
84-
var recentUploads = ['a', 'b', 'c'];
103+
it('trims the list of recent uploads but leaves the active one', function() {
104+
var finalUploads = ['1','3','4','5','6','7','8','9','10','11','key:12'];
85105

86106
var redis = new Redis({}, new FakeRedis(FakeClient.extend({
87107
get: function(key) {
108+
if (key == 'key:current') {
109+
return Promise.resolve('1');
110+
}
88111
return Promise.resolve(null);
89112
},
90-
lpush: function(key, tag) {
91-
recentUploads.push(key + tag);
92-
},
93-
ltrim: function() {
94-
recentUploads.pop();
113+
zrange: function() {
114+
return this.recentRevisions.slice(0,2);
95115
}
96116
})));
97117

98-
var promise = redis.upload('key', 'value');
118+
redis._client.recentRevisions = ['1','2','3','4','5','6','7','8','9','10','11'];
119+
120+
var promise = redis.upload('key', '12', 'value');
99121
return assert.isFulfilled(promise)
100122
.then(function() {
101-
assert.equal(recentUploads.length, 3);
123+
assert.equal(redis._client.recentRevisions.length, 11);
124+
assert.deepEqual(redis._client.recentRevisions, finalUploads);
102125
});
103126
});
104127

@@ -140,14 +163,14 @@ describe('redis', function() {
140163

141164
describe('#activate', function() {
142165
it('rejects if the revisionKey doesn\t exist in list of uploaded revisions', function() {
143-
var recentRevisions = ['a', 'b', 'c'];
144-
145166
var redis = new Redis({}, new FakeRedis(FakeClient.extend({
146-
lrange: function() {
147-
return recentRevisions;
167+
zrevrange: function() {
168+
return this.recentRevisions;
148169
}
149170
})));
150171

172+
redis._client.recentRevisions = ['a', 'b', 'c'];
173+
151174
var promise = redis.activate('key-prefix', 'revision-key');
152175
return assert.isRejected(promise)
153176
.then(function(error) {
@@ -156,20 +179,17 @@ describe('redis', function() {
156179
});
157180

158181
it('resolves and sets the current revision to the revision key provided', function() {
159-
var recentRevisions = ['a', 'b', 'c'];
160182
var redisKey, redisValue;
161183

162-
163184
var redis = new Redis({}, new FakeRedis(FakeClient.extend({
164-
lrange: function() {
165-
return recentRevisions;
166-
},
167185
set: function(key, value) {
168186
redisKey = key;
169187
redisValue = value;
170188
}
171189
})));
172190

191+
redis._client.recentRevisions = ['a', 'b', 'c'];
192+
173193
var promise = redis.activate('key-prefix', 'c');
174194
return assert.isFulfilled(promise)
175195
.then(function() {
@@ -181,16 +201,11 @@ describe('redis', function() {
181201

182202
describe('#fetchRevisions', function() {
183203
it('lists the last existing revisions', function() {
184-
var recentRevisions = ['a', 'b', 'c'];
185-
186204
var redis = new Redis({}, new FakeRedis(FakeClient.extend({
187-
lrange: function() {
188-
return recentRevisions;
189-
},
190-
get: function() {
191-
}
192205
})));
193206

207+
redis._client.recentRevisions = ['a', 'b', 'c'];
208+
194209
var promise = redis.fetchRevisions('key-prefix');
195210
return assert.isFulfilled(promise)
196211
.then(function(result) {
@@ -213,18 +228,16 @@ describe('redis', function() {
213228
});
214229

215230
it('lists revisions and marks the active one', function() {
216-
var recentRevisions = ['a', 'b'];
217231
var currentRevision = 'b';
218232

219233
var redis = new Redis({}, new FakeRedis(FakeClient.extend({
220-
lrange: function() {
221-
return recentRevisions;
222-
},
223234
get: function() {
224235
return currentRevision;
225236
}
226237
})));
227238

239+
redis._client.recentRevisions = ['a', 'b'];
240+
228241
var promise = redis.fetchRevisions('key-prefix');
229242
return assert.isFulfilled(promise)
230243
.then(function(result) {

0 commit comments

Comments
 (0)