-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcktdoc.go
152 lines (120 loc) · 5.3 KB
/
cktdoc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
Peer/Circuit/Fragment Design overview
This is an alternative API to the Message and net/rpc APIs
provided by the rpc25519 package. It is a layer
on top of the Message API.
Motivated by filesystem syncing, we envision a system that can
both stream efficiently and utilize the same code
on the client as on the server.
Syncing a filesystem needs efficient stream transmission.
The total data far exceeds what will fit in any single
message, and updates may be continuous or lumpy.
We don't want to wait for one "call"
to finish its round trip. We just want to
send data when we have it. Hence the
API is based on one-way messages and is asynchronous
in that the methods and channels involved do
not wait for network round trips to complete.
Once established, a circuit between peers
is designed to persist until deliberately closed.
A circuit can then handle any number of Fragments
of data during its lifetime.
To organize communications, a peer can maintain
multiple circuits, either with the same peer
or with any number of other peers. We can then
easily handle any arbitrary network topology.
Even between just two peers, multiple persistent
channels facilities code organization. One
could use a channel per file being synced,
for instance. Multiple large files being
scanned and their diffs streamed at once,
in parallel, becomes practical.
Go's features enables such a design. By using
lightweight goroutines and channels,
circuit persistence is inexpensive and
supports any number of data streams with markedly
different lifetimes and update rates,
over long periods.
Symmetry of code deployment is also a natural
requirement. This is the git model. When syncing
two repositories, the operations needed are
the same on both sides, no matter who
initiated or whether a push or pull was
requested. Hence we want a way to register
the same functionality on the client as on the server.
This is not available in a typical RPC package.
Peer/Circuit/Fragment API essentials (utility methods omitted for compactness)
The user implements and registers a PeerServiceFunc callback
with the Client and/or Server. There are multiple examples of
this in the test files. Once registered, from within your
PeerServiceFunc implementation:
A) To establish circuits with new peers, use
1. NewCircuitToPeerURL() for initiating a new circuit to a new peer.
2. <-newCircuitCh to receive new remote initiated Circuits.
B) To create additional circuits with an already connected peer:
1. Circuit.NewCircuit adds a new circuit with an existing remote peer, no URL needed.
2. They get notified on <-newCircuitCh too.
C) To communicate over a Circuit:
1. get regular messages (called Fragments) from <-Circuit.Reads
2. get error messages from <-Circuit.Errors
3. send messages with Circuit.SendOneWay(). It never blocks.
4. Close() the circuit and both the local and remote Circuit.Context will be cancelled.
// Circuit has other fields, but this is the essential interface:
type Circuit struct {
Reads <-chan *Fragment
Errors <-chan *Fragment
Close() // when done
// If you want a new Circuit with the same remote peer:
NewCircuit() (ckt *Circuit, ctx context.Context, err error) // make 2nd, 3rd.
}
// Your PeerServiceFunc gets a pointer to its *LocalPeer as its first argument.
// LocalPeer is actually a struct, but you can think of it as this interface:
type LocalPeer interface {
NewCircuitToPeerURL(peerURL string, frag *Fragment,
errWriteDur time.Duration) (ckt *Circuit, ctx context.Context, err error)
}
// As above, users write PeerServiceFunc callbacks to create peers.
// This is the full type:
type PeerServiceFunc func(myPeer *LocalPeer, ctx0 context.Context, newCircuitCh <-chan *Circuit) error
// Fragment is the data packet transmitted over Circuits between Peers.
type Fragment struct {
// system metadata
FromPeerID string
ToPeerID string
CircuitID string
Serial int64
Typ CallType
ServiceName string
// user supplied data
FragOp int
FragSubject string
FragPart int64
Args map[string]string
Payload []byte
Err string
}
D) boostrapping:
Here is how to register your Peer implemenation and start
it up (from outside the PeerServiceFunc callback). The PeerAPI
is available via Client.PeerAPI or Server.PeerAPI.
The same facilities are available on either. This symmetry
was a major motivating design point.
1. register:
PeerAPI.RegisterPeerServiceFunc(peerServiceName string, peer PeerServiceFunc) error
2. start a previously registered PeerServiceFunc locally or remotely:
PeerAPI.StartLocalPeer(
ctx context.Context,
peerServiceName string) (lp *LocalPeer, err error)
Starting a remote peer must also specify the host:port remoteAddr
of the remote client/server. The user can call the RemoteAddr()
method on the Client/Server to obtain this.
PeerAPI.StartRemotePeer(
ctx context.Context,
peerServiceName string,
remoteAddr string, // host:port
waitUpTo time.Duration,
) (remotePeerURL, remotePeerID string, err error)
The returned URL can be used in LocalPeer.NewCircuitToPeerURL() calls,
for instance on myPeer inside the PeerServiceFunc callback.
*/
package rpc25519