Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

do not cache function sets #16

Merged
merged 5 commits into from

2 participants

@Raynos

Sets defined by filter functions may be impure. For example

function makeSet(foo) {
  return doc.createSet(function (s) { return s.foo === foo })
}

So it's not safe to cache them by their key

@dominictarr
Owner

please explain in more detail, what is the problem, and how do you fix it?

@Raynos

The problem is that if you create multiple sets then each time you create a new one crdt checks whether it has it already.

The problem is that with filter functions there is no notion of "has it already" where as with createSet("key", "value") there is. With the latter it's save to just return the set because it contains the same thing.

WIth a filter function it's not save to cache or return a cached set. This is because there is no way to translate a function into the set of items it contains. A pure function could be translated but an impure function can't be cached.

The way the current implementation in this PR fixes this issue is by simply not caching sets which are defined by functions

@Raynos

Also whitespace changes everywhere because my text editor hates your text editor :/

@dominictarr
Owner

hmm, by impure function you mean a function with side effects? or a function that has free variables that change (due to scope) or all of the above?

@Raynos

@dominictarr impure means "when given same inputs as arguments to function does not produce same outputs".

Which means it has state basically.

The fact that it may cause a side effect but always return same output is an example of an impure function that's safe to cache but that doesn't matter because it's not safe to cache all functions.

@dominictarr dominictarr merged commit 18c22d7 into from
@dominictarr
Owner

thanks, merged into 3.3.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 43 additions and 17 deletions.
  1. +3 −0  README.md
  2. +25 −17 doc.js
  3. +15 −0 test/sets.js
View
3  README.md
@@ -99,6 +99,9 @@ var cheeses = doc.createSet(function (state) {
})
```
+A filter function should just be a more expressive filter and
+shouldn't be a stateful function
+
### Doc#createSeq (key, value)
same as `Doc#createSet` except that seqs have a significant order.
View
42 doc.js
@@ -24,7 +24,7 @@ module.exports = Doc
use a anti-tombstone to show something is alive.
breathing: count. -- updated by an authority.
set breathing to 0 to kill something.
-
+
if a node has rows that have been garbage collected on the server,
it will be obvious from the value of breathing.
@@ -33,12 +33,12 @@ module.exports = Doc
node reconnects.
server updates the node, but only increments _breathing for some rows.
-
+
clearly, the nodes that do not have an upto date _breathing are either
dead, or where created by the node while it was offline.
would breathing need to be a vector clock?
-
+
if the disconneded node is still updating the rows,
maybe it shouldn't be deleted, that is, undeleted.
@@ -55,7 +55,7 @@ function Doc (id) {
if (!(this instanceof Doc)) return new Doc(id)
//the id of the doc refers to the instance.
//that is, to the node.
- //it's used to identify a node
+ //it's used to identify a node
// this.id = id || '#' + Math.round(Math.random()*1000)
this.rows = {}
this.hist = {}
@@ -95,7 +95,7 @@ Doc.prototype._add = function (id, source, change) {
}
Doc.prototype.timeUpdated = function (row, key) {
- var h = this.hist[row.id]
+ var h = this.hist[row.id]
if(!h) return
return h[key][2]
}
@@ -136,29 +136,29 @@ Doc.prototype.applyUpdate = function (update, source) {
//remember the most recent update from each node.
//now handled my scuttlebutt.
// if(!row.validate(changes)) return
-
+
for(var key in changes) {
var value = changes[key]
if(!hist[key] || order(hist[key], update) < 0) {
if(hist[key]) this.emit('_remove', hist[key])
hist[key] = update
changed[key] = changes[key]
- emit = true
+ emit = true
}
}
-// probably, there may be mulitple sets that listen to the same key,
+// probably, there may be mulitple sets that listen to the same key,
// but activate on different values...
//
-// hang on, in the mean time, I will probably only be managing n < 10 sets.
-// at once,
+// hang on, in the mean time, I will probably only be managing n < 10 sets.
+// at once,
merge(row.state, changed)
for(var k in changed)
- this.sets.emit(k, row, changed)
-
+ this.sets.emit(k, row, changed)
+
if(!emit) return
-
+
if(row._new) {
this.emit('add', row)
this.emit('create', row) //alias
@@ -186,11 +186,19 @@ Doc.prototype.history = function (sources) {
}
function _set(self, key, val, type) {
- var id = key + ':' + val
- if(self.sets[id]) return self.sets[id]
- return self.sets[key + ':' + val] = new type(self, key, val)
+ var id = typeof key === 'string' && key + ':' + val
+ if(id && self.sets[id]) {
+ return self.sets[id]
+ }
+
+ var set = new type(self, key, val)
+ if (id) {
+ self.sets[id] = set
+ }
+ return set
}
+
Doc.prototype.createSet = function (key, val) {
return _set(this, key, val, Set)
}
@@ -206,7 +214,7 @@ Doc.prototype.toJSON = function () {
return j
}
//retrive a reference to a row.
-//if the row is not created yet, create
+//if the row is not created yet, create
Doc.prototype.get = function (id) {
return this.rows[id] = this.rows[id] || this._add(new Row(id), 'local')
}
View
15 test/sets.js
@@ -106,6 +106,21 @@ exports['test - filters'] = function (t) {
t.end()
}
+exports['set caching'] = function (t) {
+ var doc = new crdt.Doc()
+
+ var set1 = doc.createSet("foo", "bar")
+ var set2 = doc.createSet("foo", "bar")
+
+ a.equal(set1, set2)
+
+ var set3 = doc.createSet(function () { })
+ var set4 = doc.createSet(function () { })
+ a.notEqual(set3, set4)
+
+ t.end()
+}
+
function log(set) {
set.on('add', function (row) {
Something went wrong with that request. Please try again.