-
Notifications
You must be signed in to change notification settings - Fork 111
/
gotocol.go
172 lines (151 loc) · 4.54 KB
/
gotocol.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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// Package gotocol provides protocol support to send a variety of commands
// listener channels and types over a single channel type
package gotocol
import (
"fmt"
"sync/atomic"
"time"
)
// Impositions is the promise theory term for requests made to a service
type Impositions int
// Constant definitions for message types to be imposed on the receiver
const (
// Hello ChanToParent Name Initial noodly touch to set identity
Hello Impositions = iota
// NameDrop ChanToBuddy NameOfBuddy Here's someone to talk to
NameDrop
// Chat - ThisOften Chat to buddies time interval
Chat
// GoldCoin FromChan HowMuch
GoldCoin
// Inform loggerChan text message
Inform
// GetRequest FromChan key Simulate http inbound request
GetRequest
// GetResponse FromChan value Simulate http outbound response
GetResponse
// Put - "key value" Save the key and value
Put
// Replicate - "key value" Save a replicated copy
Replicate
// Forget - FromBuddy ToBuddy Forget link between two buddies
Forget
// Delete - key Remove key and value
Delete
// Goodbye - name // tell FSM and exit
Goodbye // test assumes this is the last and exits
numOfImpositions
)
// String handler to make imposition types printable
func (imps Impositions) String() string {
switch imps {
case Hello:
return "Hello"
case NameDrop:
return "NameDrop"
case Chat:
return "Chat"
case GoldCoin:
return "GoldCoin"
case Inform:
return "Inform"
case GetRequest:
return "GetRequest"
case GetResponse:
return "GetResponse"
case Put:
return "Put"
case Replicate:
return "Replicate"
case Forget:
return "Forget"
case Delete:
return "Delete"
case Goodbye:
return "Goodbye"
}
return "Unknown"
}
// TraceContextType needs to be exported for flow package map. For production scale use this should be 64bit, for spigo it seems ok with 32.
type TraceContextType uint32 // needs to match type conversions in func increment below
// Context for capturing dapper/zipkin style traces
type Context struct {
Trace, Parent, Span TraceContextType
}
// string formatter for context
func (ctx Context) String() string {
return fmt.Sprintf("t%vp%vs%v", ctx.Trace, ctx.Parent, ctx.Span)
}
// Route string formatter for routing part of context
func (ctx Context) Route() string {
return fmt.Sprintf("t%vp%v", ctx.Trace, ctx.Parent)
}
// fast hack for generating unique-enough contexts
var spanner TraceContextType
// return uniquely incremented TraceContextType
func increment(tc *TraceContextType) TraceContextType {
return TraceContextType(atomic.AddUint32((*uint32)(tc), 1))
}
// NewTrace using atomic increment
func NewTrace() Context {
var ctx Context
//ctx.Trace = rand.Uint32()
// NilContext is t0p0s0, so first real Trace,Parent,Span is t1p0s1
ctx.Span = increment(&spanner)
ctx.Trace = ctx.Span // trace = span with zero parent for first span in a trace
return ctx
}
// AddSpan updates to get a new span for an existing request and parent
func (ctx Context) AddSpan() Context {
ctx.Span = increment(&spanner)
return ctx
}
// NewParent sets up the parent by promoting incoming span id, and get a new spanid
func (ctx Context) NewParent() Context {
ctx.Parent = ctx.Span
return ctx.AddSpan()
}
// NilContext makes an empty context, I can't figure out how to make this a const
var NilContext Context
// Message structure used for all messages, includes a channel of itself
type Message struct {
Imposition Impositions // request type
ResponseChan chan Message // place to send response messages
Sent time.Time // time at which message was sent
Ctx Context // message context
Intention string // payload
}
func (msg Message) String() string {
return fmt.Sprintf("gotocol: %v %v %v %v", time.Since(msg.Sent), msg.Ctx, msg.Imposition, msg.Intention)
}
// Routetype information from a message
type Routetype struct {
Ctx Context
ResponseChan chan Message
State int // state machine for managing responses
}
// Route extracts routing information from a message
func (msg Message) Route() Routetype {
var r Routetype
r.Ctx = msg.Ctx
r.ResponseChan = msg.ResponseChan
return r
}
// PickRoute uses a message context to extract route from routing map
func PickRoute(rmap map[string]Routetype, msg Message) Routetype {
return rmap[msg.Ctx.Route()]
}
// Send a synchronous message
func Send(to chan<- Message, msg Message) {
if to != nil {
to <- msg
}
}
// GoSend asynchronous message send, parks it on a new goroutine until it completes
func (msg Message) GoSend(to chan Message) {
go func(c chan Message, m Message) {
if c != nil {
c <- m
}
}(to, msg)
}