Skip to content

Commit bc2f732

Browse files
committed
balancers.FromConfig
1 parent 06c2e02 commit bc2f732

File tree

4 files changed

+234
-1
lines changed

4 files changed

+234
-1
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 3.14.0
2+
* Added `balacers.FromConfig` balancers creator
3+
14
## 3.13.3
25
* Fixed linter issues
36

balancers/config.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package balancers
2+
3+
import (
4+
"encoding/json"
5+
6+
"github.com/ydb-platform/ydb-go-sdk/v3/internal/balancer"
7+
"github.com/ydb-platform/ydb-go-sdk/v3/internal/errors"
8+
)
9+
10+
type balancerType string
11+
12+
const (
13+
typeRoundRobin = balancerType("round_robin")
14+
typeRandomChoice = balancerType("random_choice")
15+
typeSingle = balancerType("single")
16+
)
17+
18+
type preferType string
19+
20+
const (
21+
preferLocalDC = preferType("local_dc")
22+
preferLocations = preferType("locations")
23+
)
24+
25+
type balancersConfig struct {
26+
Type balancerType `json:"type"`
27+
Prefer preferType `json:"prefer,omitempty"`
28+
Fallback bool `json:"fallback,omitempty"`
29+
Locations []string `json:"locations,omitempty"`
30+
}
31+
32+
type fromConfigOptionsHolder struct {
33+
fallbackBalancer balancer.Balancer
34+
errorHandler func(error)
35+
}
36+
37+
type fromConfigOption func(h *fromConfigOptionsHolder)
38+
39+
func WithParseErrorFallbackBalancer(b balancer.Balancer) fromConfigOption {
40+
return func(h *fromConfigOptionsHolder) {
41+
h.fallbackBalancer = b
42+
}
43+
}
44+
45+
func WithParseErrorHandler(errorHandler func(error)) fromConfigOption {
46+
return func(h *fromConfigOptionsHolder) {
47+
h.errorHandler = errorHandler
48+
}
49+
}
50+
51+
func FromConfig(config string, opts ...fromConfigOption) (b balancer.Balancer) {
52+
var (
53+
h = fromConfigOptionsHolder{
54+
fallbackBalancer: Default(),
55+
}
56+
c balancersConfig
57+
)
58+
59+
for _, o := range opts {
60+
o(&h)
61+
}
62+
63+
if err := json.Unmarshal([]byte(config), &c); err != nil {
64+
if h.errorHandler != nil {
65+
h.errorHandler(err)
66+
}
67+
return h.fallbackBalancer
68+
}
69+
70+
switch c.Type {
71+
case typeSingle:
72+
b = SingleConn()
73+
case typeRandomChoice:
74+
b = RandomChoice()
75+
case typeRoundRobin:
76+
b = RoundRobin()
77+
default:
78+
if h.errorHandler != nil {
79+
h.errorHandler(errors.Errorf("unknown type of balancer: %s", c.Type))
80+
}
81+
return h.fallbackBalancer
82+
}
83+
84+
switch c.Prefer {
85+
case preferLocalDC:
86+
if c.Fallback {
87+
return PreferLocalDCWithFallBack(b)
88+
}
89+
return PreferLocalDC(b)
90+
case preferLocations:
91+
if len(c.Locations) == 0 {
92+
if h.errorHandler != nil {
93+
h.errorHandler(errors.Errorf("empty locations list in balancer '%s' config", c.Type))
94+
}
95+
return h.fallbackBalancer
96+
}
97+
if c.Fallback {
98+
return PreferLocationsWithFallback(b, c.Locations...)
99+
}
100+
return PreferLocations(b, c.Locations...)
101+
default:
102+
return b
103+
}
104+
}

balancers/config_test.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package balancers
2+
3+
import (
4+
"testing"
5+
6+
"github.com/ydb-platform/ydb-go-sdk/v3/internal/balancer"
7+
"github.com/ydb-platform/ydb-go-sdk/v3/internal/conn"
8+
"github.com/ydb-platform/ydb-go-sdk/v3/internal/endpoint/info"
9+
)
10+
11+
type testBalancer struct{}
12+
13+
func (t testBalancer) Next() conn.Conn {
14+
panic("unexpected call")
15+
}
16+
17+
func (t testBalancer) Insert(conn conn.Conn) balancer.Element {
18+
panic("unexpected call")
19+
}
20+
21+
func (t testBalancer) Update(element balancer.Element, info info.Info) {
22+
panic("unexpected call")
23+
}
24+
25+
func (t testBalancer) Remove(element balancer.Element) bool {
26+
panic("unexpected call")
27+
}
28+
29+
func (t testBalancer) Contains(element balancer.Element) bool {
30+
panic("unexpected call")
31+
}
32+
33+
func TestFromConfig(t *testing.T) {
34+
for _, test := range []struct {
35+
name string
36+
config string
37+
fail bool
38+
}{
39+
{
40+
name: "empty",
41+
config: ``,
42+
fail: true,
43+
},
44+
{
45+
name: "single",
46+
config: `{
47+
"type": "single"
48+
}`,
49+
},
50+
{
51+
name: "round_robin",
52+
config: `{
53+
"type": "round_robin"
54+
}`,
55+
},
56+
{
57+
name: "random_choice",
58+
config: `{
59+
"type": "random_choice"
60+
}`,
61+
},
62+
{
63+
name: "prefer_local_dc",
64+
config: `{
65+
"type": "random_choice",
66+
"prefer": "local_dc"
67+
}`,
68+
},
69+
{
70+
name: "prefer_unknown_type",
71+
config: `{
72+
"type": "unknown_type",
73+
"prefer": "local_dc"
74+
}`,
75+
fail: true,
76+
},
77+
{
78+
name: "prefer_local_dc_with_fallback",
79+
config: `{
80+
"type": "random_choice",
81+
"prefer": "local_dc",
82+
"fallback": true
83+
}`,
84+
},
85+
{
86+
name: "prefer_locations",
87+
config: `{
88+
"type": "random_choice",
89+
"prefer": "locations",
90+
"locations": ["AAA", "BBB", "CCC"]
91+
}`,
92+
},
93+
{
94+
name: "prefer_locations_with_fallback",
95+
config: `{
96+
"type": "random_choice",
97+
"prefer": "locations",
98+
"locations": ["AAA", "BBB", "CCC"],
99+
"fallback": true
100+
}`,
101+
},
102+
} {
103+
t.Run(test.name, func(t *testing.T) {
104+
var (
105+
actErr error
106+
fallback testBalancer
107+
)
108+
b := FromConfig(
109+
test.config,
110+
WithParseErrorFallbackBalancer(fallback),
111+
WithParseErrorHandler(func(err error) {
112+
actErr = err
113+
}),
114+
)
115+
if test.fail && actErr == nil {
116+
t.Fatalf("expected error, but it not hanled")
117+
}
118+
if !test.fail && actErr != nil {
119+
t.Fatalf("unexpected error: %v", actErr)
120+
}
121+
if test.fail && b != fallback {
122+
t.Fatalf("unexpected balancer: %v", b)
123+
}
124+
})
125+
}
126+
}

internal/meta/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
package meta
22

33
const (
4-
Version = "ydb-go-sdk/3.13.3"
4+
Version = "ydb-go-sdk/3.14.0"
55
)

0 commit comments

Comments
 (0)