Repository navigation
Expand file tree
/
Copy pathrpc_server.go
More file actions
152 lines (123 loc) · 3.74 KB
/
Copy pathrpc_server.go
File metadata and controls
152 lines (123 loc) · 3.74 KB
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
package dchan
import (
"context"
"errors"
"github.com/hashicorp/raft"
p "github.com/m4tth3/dchan/proto"
"google.golang.org/protobuf/types/known/emptypb"
)
var (
ErrNoLongerReceiving = errors.New("no longer receiving")
)
type receiverManager interface {
// Returns the receiver, a function to decrement the sending count, and a boolean
// indicating if the receiver exists.
//
// If the receiver is grabbed, the sending count is incremented.
// The function should be called when the message is sent to the receiver
// or the context is done to decrement the sending count.
getReceiver(namespace Namespace) (rch *rchannel, dec func() int32, ok bool)
}
type server struct {
rm receiverManager
raft *raft.Raft
client *client
p.UnsafeDChanServiceServer // Ensure compilation
}
var _ p.DChanServiceServer = &server{}
// TODO: make this support chunking instead
func (r server) Receive(ctx context.Context, req *p.ReceiveRequest) (*p.ReceiveResponse, error) {
namespace := Namespace(req.GetNamespace())
data := req.GetData()
receiver, dec, ok := r.rm.getReceiver(namespace)
if !ok {
return &p.ReceiveResponse{Received: false}, nil
}
v, err := gobDecode(data)
if err != nil {
return nil, err
}
// The message is sent to the channel when the receiver is ready to receive.
// This allows for client backpressure.
//
// The client can explicitly set deadlines to avoid blocking.
select {
case receiver.ch <- v:
case <-receiver.ctx.Done(): // No more receivers, stop sending.
if count := dec(); count == 0 {
// Last sender, notify close goroutine to close the channel.
receiver.closeCh <- struct{}{}
}
return &p.ReceiveResponse{Received: false}, nil // Reject
}
dec()
return &p.ReceiveResponse{Received: true}, nil
}
func (r server) RegisterReceiver(ctx context.Context, req *p.ReceiverRequest) (*emptypb.Empty, error) {
cmd := fsmCmd{
Type: registerReceiver,
Namespace: Namespace(req.GetNamespace()),
ServerId: ServerID(req.GetServerId()),
Requester: ServerID(req.GetRequester()),
}
encodedCmd, err := encodeFsmCmd(cmd)
if err != nil {
return nil, err
}
future := r.raft.Apply(encodedCmd, 0)
if err := future.Error(); err != nil {
return nil, err
}
return &emptypb.Empty{}, nil
}
func (r server) UnregisterReceiver(ctx context.Context, req *p.ReceiverRequest) (*emptypb.Empty, error) {
cmd := fsmCmd{
Type: unregisterReceiver,
Namespace: Namespace(req.GetNamespace()),
ServerId: ServerID(req.GetServerId()),
Requester: ServerID(req.GetRequester()),
}
encodedCmd, err := encodeFsmCmd(cmd)
if err != nil {
return nil, err
}
future := r.raft.Apply(encodedCmd, 0)
// Block until the command is applied.
if err := future.Error(); err != nil {
return nil, err
}
return &emptypb.Empty{}, nil
}
func (r server) AddVoter(ctx context.Context, req *p.ServerInfo) (*emptypb.Empty, error) {
if r.raft.State() != raft.Leader {
if leader, err := r.client.getLeaderClient(); err != nil {
return nil, err
} else {
// Forward the request to the leader.
return leader.AddVoter(ctx, req)
}
}
id := raft.ServerID(req.GetIdAddress())
address := raft.ServerAddress(req.GetIdAddress())
future := r.raft.AddVoter(id, address, 0, 0)
if err := future.Error(); err != nil {
return nil, err
}
return &emptypb.Empty{}, nil
}
func (r server) RemoveVoter(ctx context.Context, req *p.ServerInfo) (*emptypb.Empty, error) {
if r.raft.State() != raft.Leader {
if leader, err := r.client.getLeaderClient(); err != nil {
return nil, err
} else {
// Forward the request to the leader.
return leader.RemoveVoter(ctx, req)
}
}
id := raft.ServerID(req.GetIdAddress())
future := r.raft.RemoveServer(id, 0, 0)
if err := future.Error(); err != nil {
return nil, err
}
return &emptypb.Empty{}, nil
}