Skip to content

Commit 5c3f94e

Browse files
committed
[DONUT-45] removed an unnecessary closure, updated readme, added base module for default values of arguments
1 parent a6081c3 commit 5c3f94e

File tree

6 files changed

+143
-121
lines changed

6 files changed

+143
-121
lines changed

README.md

+58-55
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ protocol before continuing too much further.
1111

1212
## Overview ##
1313

14-
Simple-Scuttle exports a class - soit `Gossip` - whose instances are
15-
transform streams in objectMode. This stream maintains several data structures
16-
with which it manages state (see [`Gossip.state`](#gossipstate)) and the
17-
propagation of state changes (implemented with [`Gossip.digest`](#gossipdigest) and
18-
[`Gossip.history`](#gossiphistory)).
14+
Simple-Scuttle exports a class - soit `Gossip` - whose instances are transform
15+
streams in objectMode. `Gossip` instances maintain several data structures with
16+
which they manages state (see [`Gossip.state`](#gossipstate)) and the
17+
propagation of state changes (implemented with [`Gossip.clock`](#gossipclock)
18+
and [`Gossip.history`](#gossiphistory)).
1919

2020
Rather than implementing several parallel streams, `Gossip` instances choose
2121
logical branches based on the semantics of the objects written to them-- the
@@ -29,26 +29,28 @@ documented in the [Expected Objects](#expected-objects) section.
2929
```js
3030
Gossip(
3131
String id
32-
, Integer mtu | false
33-
, Integer max_history | false
34-
, Function sort(Update A, Update B) -> Bool | false
32+
, Integer mtu | null
33+
, Integer max_history | null
34+
, Function should_apply | null
35+
, Function sort | null
3536
) -> gossip
3637
```
3738

3839
- `id`: The unique identifier for each `Gossip` instance.
3940
- `mtu`: How many messages the network can handle at once-- this is used to set
4041
[opts.highWaterMark](http://nodejs.org/api/stream.html#stream_new_stream_readable_options). Defaults to 10 if falsey.
41-
- `max_history`: How many deltas to store before we begin to forget old ones. Such concerns are absent from the paper, but they seem important to me. Defaults to 10 if falsey.
42-
- `sort`: A function which describes how to resolve
43-
versioning ties between when two deltas disagree on a key-value pair. It's
44-
return value indicates whether its first argument should take primacy.
45-
Defaults to sort by version and breaking ties with lexical ordering of IDs.
42+
- `max_history`: How many updates to store before we begin to forget old ones. Such concerns are absent from the paper, but they seem important to me. Defaults to 10 if falsey.
43+
- `should_apply` `(gossip, update)` -> `Boolean`: A function which determines
44+
whether or not a given update should be applied.
45+
- `sort`: A function which describes how to order updates. Has the same
46+
signature as javascripts
47+
[Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort), and will be called by Array.sort under the hood.
4648

4749
## Expected Objects ##
4850

49-
Gossip instances expect objects written to them (with `gossip.write`) to either be `digest`s or `delta`s.
51+
Gossip instances expect objects written to them (with `gossip.write`) to either be `digest`s or `delta`s. Note that `delta`s are also referred to as `updates`.
5052

51-
Digests look like:
53+
####digests####
5254

5355
```js
5456
var digest = {
@@ -78,43 +80,43 @@ from calling `delete Gossip.state[key]` at every node, and then handling
7880
avoid designing a system in which the number of keys can grow without bound.
7981
Implementing a safe delete method remains a [To Do](#todo)
8082

83+
####deltas####
8184
The other kind of object, the delta, is an object that appears like the
8285
following:
8386

8487
```js
8588

8689
var delta = {
87-
key: any_obj_which_can_be_a_key
88-
, value: some_serializable_value
89-
, source_id: source_id
90-
, version: version_number_for_this_update
90+
key: 'age'
91+
, value: 100
92+
, source_id: '#A'
93+
, version: 10
9194
}
9295
```
9396

9497
This says: "source `source_id` thought `key` mapped to `value` at `version`."
95-
There is still some ambiguity here about when to apply this update to the local
96-
state, and indeed, [npm.im/scuttlebutt][] leaves this specification to the
97-
client. At current, the update is always applied, so long as `version` is
98-
greater than the current version for the key.
98+
The `should_apply` argument allows the user to specify whether or not this
99+
update should be applied.
99100

100101
## Methods ##
101102

102103
`Gossip` instances are [Transform
103104
Streams](http://nodejs.org/api/stream.html#stream_class_stream_transform_1), so
104105
they implement all of the methods and events as described in the node core
105-
documentation.
106+
documentation. In addition, there are a few methods specific to this purpose:
106107

107108
###`Gossip.set(key, value) -> null`###
108109

109110
This method applies a local delta, simply setting the given key to the given
110-
value in the local instance, giving it the appropriate `version` number and
111+
value in the local instance, and tacking on the appropriate `version` number and
111112
`source_id`.
112113

113114
###`Gossip.get(key) -> {version: <version>, value: <obj>} `###
114115

115116
A method which provides convenient lookup for arbitrary keys. If
116117
`Gossip.state` has no entry for a given key, this method returns
117-
`{version: -Infinity, value: null}`.
118+
`{version: -Infinity, value: null}`. Otherwise it returns `{version: version,
119+
value: value}`
118120

119121
###`Gossip.gossip() -> undefined`###
120122

@@ -133,24 +135,25 @@ version number, so `state[key]` -> `{version: version, value: value}`
133135

134136
###`Gossip.version`###
135137

136-
The highest version number the `Gossip` instance has seen. This is proportional
137-
to the number of updates made to this instance.
138+
The highest version number the `Gossip` instance has seen (both locally and
139+
from other instances)
138140

139141
###`Gossip.history`###
140142

141-
An object for keeping track of updates, and replaying updates from a given peer
142-
on demand. Updates are transmitted individually via the `Gossip`'s streaming
143-
methods.
143+
An object for keeping track of deltas, and replaying deltas from a given peer
144+
on demand. `delta` objects are transmitted individually via the `Gossip`'s
145+
streaming methods.
144146

145147
####`Gossip.history.write(key, value, source_id, version)`####
146148

147149
Write a new delta to the history. The delta is recorded into
148150
`Gossip.history.memory`, an array of deltas, which is then sorted via `sort`
149-
argument to the `Gossip` constructor. Next An `update` event is emitted with
150-
the delta as its argument, which allows the client to take action prior to
151-
pruning the `memory` array to `max_history`'s length.
151+
argument to the `Gossip` constructor. Next `Gossip.history` emits an `"update"`
152+
event is emitted with the delta as its argument. This event is emitted to allow
153+
the client to take action prior to pruning the `memory` array to
154+
`max_history`'s length.
152155

153-
####`Gossip.history.news(id, version)` -> [Array Deltas](#%CE%B4)####
156+
####`Gossip.history.news(id, version)` -> [`Array Deltas`](#deltas)####
154157

155158
Returns an array of `delta`s which came from a source with unique identifier
156159
matching `id`, and which occurred after `version`.
@@ -165,47 +168,47 @@ Objects](#expected-objects)
165168
For a paper defining vector clocks, see [here][vector-clock-paper]
166169

167170

168-
####`Gossip.digest.clock`####
171+
####`Gossip.clock.clock`####
169172

170-
The vector clock itself-- a map from `source_ids` to version numbers, keeping
173+
The vector clock-- a map from `source_ids` to version numbers, keeping
171174
track of the last update this `Gossip` instance has seen from any of its peers.
172175

173-
####`Gossip.digest.get(id)` -> `Integer version'####
176+
####`Gossip.clock.get(id)` -> `Integer version'####
174177

175178
Returns the version number for the specified `id`, or `-Infinity` if it cannot be
176179
found.
177180

178-
####`Gossip.digest.set(source, version)`####
181+
####`Gossip.clock.set(source, version)`####
179182

180183
Sets the specified `source` to the specified `version` number in the
181-
`Gossip.digest.clock` object.
184+
`Gossip.clock.clock` object.
182185

183-
####`Gossip.digest.create()`####
186+
####`Gossip.clock.create()`####
184187

185188
Return a randomly ordered array of `digest` stream objects for each `source` in th clock. See [Expected Objects][#expected-objects].
186189

187190
# Relation to [npm.im/scuttlebutt][] #
188191

189-
This was inspired by [Dominic Tarr's scuttlebut
190-
module][npm.im/scuttlebutt], which, though totally awesome,
191-
I found a little hard to parse. So in order to understand [the
192-
paper][paper] , I wrote my
193-
own module to implement the protocol. As such this module bears great fidelity
194-
to the paper-- many decision that [npm.im/scuttlebutt][] leave to the client
195-
are in fact specified in the paper that describes the protocol. Others, such as
196-
resolution of delta conflicts, are left to the user through to specify through
197-
function argument rather than subclassing. I intended to replicate terminology
198-
from the paper faithfully, subject of course to the restrictions imposed by the
199-
format and language (javascript rather than maths).
192+
This was inspired by [Dominic Tarr's scuttlebut module][npm.im/scuttlebutt],
193+
which, though totally awesome, I found a little hard to parse. So in order
194+
to understand [the paper][paper] , I wrote my own module to implement the
195+
protocol. As such this module bears great fidelity to the paper-- many
196+
decision that [npm.im/scuttlebutt][] leave to the client are in fact
197+
specified in the paper that describes the protocol. Others, such as
198+
resolution of delta conflicts, are left to the user to specify through
199+
function argument rather than subclassing. I intended to replicate
200+
terminology from the paper faithfully, subject of course to the
201+
restrictions imposed by the format and language (javascript rather than
202+
maths). The other difference is that this implementation does not
203+
require that the user specify a schema (the set of possible keys) prior to
204+
instantiating the `Gossip` object.
200205

201206
# TODO: #
202-
- Make sure Gossip.version is updated if need be by applicaiton of external deltas.
203207
- A parent constructor to ensure uniquenes of id, uniformity of mtu, etc.
204208
- Investigate whether history's memory attribute should be an array that is
205209
`.sort(fn)`-ed, or a custom implementation, such as [this
206210
one][cross-filter-sort].
207-
- Find a way to do safe deletes.
208-
- Make sure updates are applied only when they should be.
211+
- Find a way to safely delete keys from the state.
209212

210213
[npm.im/scuttlebutt]: https://npmjs.org/package/scuttlebutt
211214
[paper]: http://www.cs.cornell.edu/home/rvr/papers/flowgossip.pdf

lib/base.js

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
2+
module.exports = {}
3+
module.exports.should_apply = should_apply
4+
module.exports.sort = sort
5+
module.exports.mtu = 10
6+
module.exports.max_history = 10
7+
8+
function should_apply(gossip, update) {
9+
var current = gossip.state[update.key]
10+
11+
if(!current) {
12+
current = {}
13+
current.key = update.key
14+
current.value = update.value
15+
current.source_id = gossip.id
16+
current.version = -Infinity
17+
}
18+
19+
return sort(update, current) > 0
20+
}
21+
22+
function sort(A, B) {
23+
var Afirst
24+
25+
if(A.version === B.version) {
26+
Afirst = A.source_id < B.source_id
27+
} else {
28+
Afirst = A.version < B.version
29+
}
30+
31+
return Afirst ? -1 : 1
32+
}
33+

0 commit comments

Comments
 (0)