Skip to content

Commit 0194b13

Browse files
committed
finished etcd migration
1 parent 958e288 commit 0194b13

File tree

10 files changed

+321
-63
lines changed

10 files changed

+321
-63
lines changed

Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ endif
1414

1515
test_rule: # @HELP execute tests
1616
@echo "executing tests"
17-
GOTRACEBACK=all go test $(TESTARGS) -count=5 -timeout=120s -race ./test/...
18-
GOTRACEBACK=all go test $(TESTARGS) -count=5 -timeout=120s -tags batchtest -race ./test/...
17+
GOTRACEBACK=all go test $(TESTARGS) -count=1 -timeout=60s -race ./test/...
18+
GOTRACEBACK=all go test $(TESTARGS) -count=1 -timeout=60s -tags batchtest -race ./test/...
1919

2020
lint: # @HELP lint files and format if possible
2121
@echo "executing linter"

go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ require (
1111
github.com/dustin/go-humanize v1.0.0 // indirect
1212
github.com/gogo/protobuf v1.3.2 // indirect
1313
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
14-
github.com/google/uuid v1.2.0 // indirect
14+
github.com/google/uuid v1.2.0
1515
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 // indirect
1616
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
1717
github.com/jonboulle/clockwork v0.2.2 // indirect
1818
github.com/prometheus/client_golang v1.9.0 // indirect
1919
github.com/prometheus/common v0.15.0
2020
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
21+
go.uber.org/goleak v1.1.10
2122
go.uber.org/zap v1.16.0 // indirect
2223
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
2324
google.golang.org/genproto v0.0.0-20210212180131-e7f2df4ecc2d // indirect

go.sum

+3
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,8 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
334334
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
335335
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
336336
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
337+
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
338+
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
337339
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
338340
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
339341
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
@@ -450,6 +452,7 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
450452
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
451453
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
452454
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
455+
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
453456
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
454457
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
455458
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=

internal/coordinator.go

+44-7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ import (
77
"time"
88
)
99

10+
// A single write requests to be applied to etcd.
11+
type request struct {
12+
// Issuer writer context.
13+
ctx context.Context
14+
15+
// Event to be sent to etcd.
16+
event Event
17+
18+
// Channel to send response back.
19+
response chan error
20+
}
21+
1022
// Configuration for the coordinator.
1123
type CoordinatorConfiguration struct {
1224
// Each Coordinator will handle only a single partition.
@@ -36,7 +48,7 @@ type Coordinator interface {
3648
Watch(received chan<- Event) error
3749

3850
// Issues an Event.
39-
Write(ctx context.Context, event Event) error
51+
Write(ctx context.Context, event Event) <-chan error
4052
}
4153

4254
// Create a new Coordinator using the given configuration.
@@ -57,7 +69,9 @@ func NewCoordinator(configuration CoordinatorConfiguration) (Coordinator, error)
5769
kv: kv,
5870
ctx: ctx,
5971
cancel: cancel,
72+
writeChan: make(chan request),
6073
}
74+
configuration.Handler.Spawn(coord.writer)
6175
return coord, nil
6276
}
6377

@@ -77,6 +91,25 @@ type EtcdCoordinator struct {
7791

7892
// The key-value entry point for issuing requests.
7993
kv clientv3.KV
94+
95+
// Channel to receive write requests.
96+
writeChan chan request
97+
}
98+
99+
// Listen and apply write requests.
100+
// This will keep running while the application context is available.
101+
// Receiving commands through the channel will ensure that they are
102+
// applied synchronously to the etcd.
103+
func (e *EtcdCoordinator) writer() {
104+
for {
105+
select {
106+
case <-e.ctx.Done():
107+
return
108+
case req := <-e.writeChan:
109+
_, err := e.kv.Put(req.ctx, req.event.Key, string(req.event.Value))
110+
req.response <- err
111+
}
112+
}
80113
}
81114

82115
// Starts a new coroutine for watching the Coordinator partition.
@@ -87,12 +120,11 @@ type EtcdCoordinator struct {
87120
func (e *EtcdCoordinator) Watch(received chan<- Event) error {
88121
watchChan := e.cli.Watch(e.ctx, e.configuration.Partition)
89122
watchChanges := func() {
90-
defer e.cancel()
91-
for {
123+
for response := range watchChan {
92124
select {
93125
case <-e.ctx.Done():
94126
return
95-
case response := <-watchChan:
127+
default:
96128
e.handleResponse(response, received)
97129
}
98130
}
@@ -102,9 +134,14 @@ func (e *EtcdCoordinator) Watch(received chan<- Event) error {
102134
}
103135

104136
// Write the given event using the KV interface.
105-
func (e *EtcdCoordinator) Write(ctx context.Context, event Event) error {
106-
_, err := e.kv.Put(ctx, event.Key, string(event.Value))
107-
return err
137+
func (e *EtcdCoordinator) Write(ctx context.Context, event Event) <-chan error {
138+
res := make(chan error)
139+
e.writeChan <- request{
140+
ctx: ctx,
141+
event: event,
142+
response: res,
143+
}
144+
return res
108145
}
109146

110147
// Stop the etcd client connection.

internal/core.go

+30-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package internal
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"io"
8+
"time"
79
)
810

911
var (
@@ -20,6 +22,9 @@ type CoreConfiguration struct {
2022

2123
// Server address for the Coordinator.
2224
Server string
25+
26+
// Default timeout to be applied when handling channels.
27+
DefaultTimeout time.Duration
2328
}
2429

2530
// Holds all flags used to manage the Core state.
@@ -68,6 +73,9 @@ type ReltCore struct {
6873

6974
// Flags for handling state.
7075
flags CoreFlags
76+
77+
// Core configuration parameters.
78+
configuration CoreConfiguration
7179
}
7280

7381
// Create a new ReltCore using the given configuration.
@@ -92,15 +100,31 @@ func NewCore(configuration CoreConfiguration) (Core, error) {
92100
finish: cancel,
93101
handler: handler,
94102
coord: coord,
95-
output: make(chan Message),
103+
output: make(chan Message, 100),
96104
flags: CoreFlags{
97105
shutdown: Flag{},
98106
watching: Flag{},
99107
},
108+
configuration: configuration,
100109
}
101110
return core, nil
102111
}
103112

113+
// After receiving an event from the Coordinator, the element
114+
// should be parsed and sent back through the output channel.
115+
func (r *ReltCore) publishMessageToListener(message Message) {
116+
select {
117+
case <-r.ctx.Done():
118+
return
119+
case r.output <- message:
120+
return
121+
case <-time.After(r.configuration.DefaultTimeout):
122+
// TODO: create something to handle timed out responses.
123+
fmt.Printf("not published %#v\n", message)
124+
return
125+
}
126+
}
127+
104128
// The Listen method can be called only once.
105129
// This will start a new goroutine that receives updates
106130
// from the Coordinator and parse the information to a Message object.
@@ -112,7 +136,7 @@ func (r *ReltCore) Listen() (<-chan Message, error) {
112136
}
113137

114138
if r.flags.watching.Inactivate() {
115-
events := make(chan Event)
139+
events := make(chan Event, 100)
116140
if err := r.coord.Watch(events); err != nil {
117141
return nil, err
118142
}
@@ -123,7 +147,7 @@ func (r *ReltCore) Listen() (<-chan Message, error) {
123147
case <-r.ctx.Done():
124148
return
125149
case event := <-events:
126-
r.output <- event.toMessage()
150+
r.publishMessageToListener(event.toMessage())
127151
}
128152
}
129153
}
@@ -140,13 +164,13 @@ func (r *ReltCore) Listen() (<-chan Message, error) {
140164
func (r *ReltCore) Send(ctx context.Context, dest string, data []byte) <-chan error {
141165
response := make(chan error, 1)
142166
writeRequest := func() {
143-
defer close(response)
144167
if r.flags.shutdown.IsActive() {
145168
event := Event{
146169
Key: dest,
147170
Value: data,
148171
}
149-
response <- r.coord.Write(ctx, event)
172+
err := <-r.coord.Write(ctx, event)
173+
response <- err
150174
} else {
151175
response <- coreWasShutdown
152176
}
@@ -162,6 +186,7 @@ func (r *ReltCore) Send(ctx context.Context, dest string, data []byte) <-chan er
162186
// *This method will block until everything is finished.*
163187
func (r *ReltCore) Close() error {
164188
if r.flags.shutdown.Inactivate() {
189+
defer close(r.output)
165190
if err := r.coord.Close(); err != nil {
166191
return err
167192
}

pkg/relt/configuration.go

+16-7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package relt
22

33
import (
44
"errors"
5+
"time"
56
)
67

78
var (
@@ -11,7 +12,7 @@ var (
1112

1213
// Configuration used for creating a new instance
1314
// of the Relt.
14-
type ReltConfiguration struct {
15+
type Configuration struct {
1516
// The Relt name. Is not required, if empty a
1617
// random string will be generated to be used.
1718
// This must be unique, since it will be used to declare
@@ -31,23 +32,27 @@ type ReltConfiguration struct {
3132
// messages. If all peers are using the same exchange then
3233
// is the same as all peers are a single partition.
3334
Exchange GroupAddress
35+
36+
// Default timeout to be applied when handling asynchronous methods.
37+
DefaultTimeout time.Duration
3438
}
3539

3640
// Creates the default configuration for the Relt.
3741
// The peer, thus the queue name will be randomly generated,
3842
// the connection Url will connect to a local broker using
3943
// the user `guest` and password `guest`.
4044
// The default exchange will fallback to `relt`.
41-
func DefaultReltConfiguration() *ReltConfiguration {
42-
return &ReltConfiguration{
43-
Name: GenerateUID(),
44-
Url: "localhost:2379",
45-
Exchange: DefaultExchangeName,
45+
func DefaultReltConfiguration() *Configuration {
46+
return &Configuration{
47+
Name: GenerateUID(),
48+
Url: "localhost:2379",
49+
Exchange: DefaultExchangeName,
50+
DefaultTimeout: time.Second,
4651
}
4752
}
4853

4954
// Verify if the given Relt configuration is valid.
50-
func (c *ReltConfiguration) ValidateConfiguration() error {
55+
func (c *Configuration) ValidateConfiguration() error {
5156
if len(c.Name) == 0 {
5257
c.Name = GenerateUID()
5358
}
@@ -64,5 +69,9 @@ func (c *ReltConfiguration) ValidateConfiguration() error {
6469
return ErrInvalidConfiguration
6570
}
6671

72+
if c.DefaultTimeout.Microseconds() <= 0 {
73+
return ErrInvalidConfiguration
74+
}
75+
6776
return nil
6877
}

pkg/relt/relt.go

+10-8
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"errors"
66
"github.com/jabolina/relt/internal"
7-
"time"
87
)
98

109
var (
@@ -19,19 +18,19 @@ var (
1918
type Relt struct {
2019
// Holds the configuration about the core
2120
// and the Relt transport.
22-
configuration ReltConfiguration
21+
configuration Configuration
2322

2423
// Holds the Core structure.
2524
core internal.Core
2625
}
2726

2827
// Implements the Transport interface.
29-
func (r Relt) Consume() (<-chan internal.Message, error) {
28+
func (r *Relt) Consume() (<-chan internal.Message, error) {
3029
return r.core.Listen()
3130
}
3231

3332
// Implements the Transport interface.
34-
func (r Relt) Broadcast(ctx context.Context, message Send) error {
33+
func (r *Relt) Broadcast(ctx context.Context, message Send) error {
3534
if len(message.Address) == 0 {
3635
return ErrInvalidGroupAddress
3736
}
@@ -40,10 +39,12 @@ func (r Relt) Broadcast(ctx context.Context, message Send) error {
4039
return ErrInvalidMessage
4140
}
4241

43-
timeout, cancel := context.WithTimeout(ctx, time.Second)
42+
timeout, cancel := context.WithTimeout(ctx, r.configuration.DefaultTimeout)
4443
defer cancel()
4544

4645
select {
46+
case <-ctx.Done():
47+
return ErrContextClosed
4748
case <-timeout.Done():
4849
return ErrPublishTimeout
4950
case err := <-r.core.Send(timeout, string(message.Address), message.Data):
@@ -58,10 +59,11 @@ func (r *Relt) Close() error {
5859

5960
// Creates a new instance of the reliable transport,
6061
// and start all needed routines.
61-
func NewRelt(configuration ReltConfiguration) (*Relt, error) {
62+
func NewRelt(configuration Configuration) (*Relt, error) {
6263
conf := internal.CoreConfiguration{
63-
Partition: string(configuration.Exchange),
64-
Server: configuration.Url,
64+
Partition: string(configuration.Exchange),
65+
Server: configuration.Url,
66+
DefaultTimeout: configuration.DefaultTimeout,
6567
}
6668
core, err := internal.NewCore(conf)
6769
if err != nil {

0 commit comments

Comments
 (0)