Skip to content

Commit 39347f3

Browse files
author
Valery Piashchynski
authored
Merge pull request #59 from spiral/feature/fsm
Feature/fsm
2 parents 035d73a + fc7374c commit 39347f3

30 files changed

+1430
-891
lines changed

LICENCE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 Spiral Scout
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Makefile

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
test:
2-
go test -v -race ./tests/backoff
3-
go test -v -race ./tests/happy_scenarios
4-
go test -v -race ./tests/interfaces
5-
go test -v -race ./tests/issues
6-
go test -v -race ./tests/stress
7-
go test -v -race ./tests/disabled_vertices
2+
go test -v -race -tags=debug ./tests/backoff
3+
go test -v -race -tags=debug ./tests/happy_scenarios
4+
go test -v -race -tags=debug ./tests/interfaces
5+
go test -v -race -tags=debug ./tests/issues
6+
go test -v -race -tags=debug ./tests/stress
7+
go test -v -race -tags=debug ./tests/disabled_vertices

calculate_deps.go

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"fmt"
55
"reflect"
66

7-
"github.com/spiral/endure/structures"
87
"github.com/spiral/errors"
98
"go.uber.org/zap"
109
)
@@ -32,17 +31,17 @@ func (e *Endure) addProviders(vertexID string, vertex interface{}) error {
3231
// get the Vertex from the graph (gVertex)
3332
gVertex := e.graph.GetVertex(vertexID)
3433
if gVertex.Provides == nil {
35-
gVertex.Provides = make(map[string]structures.ProvidedEntry)
34+
gVertex.Provides = make(map[string]ProvidedEntry)
3635
}
3736

3837
// Make a slice
3938
if gVertex.Meta.FnsProviderToInvoke == nil {
40-
gVertex.Meta.FnsProviderToInvoke = make([]structures.ProviderEntry, 0, 1)
39+
gVertex.Meta.FnsProviderToInvoke = make([]ProviderEntry, 0, 1)
4140
}
4241

4342
// TODO merge function calls into one. Plugin1 -> fn's to invoke ProvideDB, ProvideDB2
4443
// Append functions which we will invoke when we start calling the structure functions after Init stage
45-
gVertex.Meta.FnsProviderToInvoke = append(gVertex.Meta.FnsProviderToInvoke, structures.ProviderEntry{
44+
gVertex.Meta.FnsProviderToInvoke = append(gVertex.Meta.FnsProviderToInvoke, ProviderEntry{
4645
/*
4746
For example:
4847
we need to invoke function ProvideDB - that will be FunctionName
@@ -64,13 +63,13 @@ func (e *Endure) addProviders(vertexID string, vertex interface{}) error {
6463
if reflect.TypeOf(vertex).Implements(ret) {
6564
tmpValue := reflect.ValueOf(vertex)
6665
tmpIsRef := isReference(ret)
67-
gVertex.Provides[typeStr] = structures.ProvidedEntry{
66+
gVertex.Provides[typeStr] = ProvidedEntry{
6867
IsReference: &tmpIsRef,
6968
Value: &tmpValue,
7069
}
7170
}
7271
} else {
73-
gVertex.Provides[typeStr] = structures.ProvidedEntry{
72+
gVertex.Provides[typeStr] = ProvidedEntry{
7473
IsReference: nil,
7574
Value: nil,
7675
}
@@ -82,19 +81,19 @@ func (e *Endure) addProviders(vertexID string, vertex interface{}) error {
8281

8382
// addEdges calculates simple graph for the dependencies
8483
func (e *Endure) addEdges() error {
85-
const Op = "add_edges"
84+
const Op = errors.Op("add_edges")
8685
// vertexID for example S2
8786
for vertexID, vrtx := range e.graph.VerticesMap {
8887
// we already checked the interface satisfaction
8988
// and we can safely skip the OK parameter here
9089
init, _ := reflect.TypeOf(vrtx.Iface).MethodByName(InitMethodName)
9190

9291
if init.Type == nil {
93-
e.logger.Fatal("init method is absent in struct", zap.String("vertex id", vertexID))
94-
return errors.E(Op, fmt.Errorf("init method is absent in struct"))
92+
e.logger.Fatal("internal_init method is absent in struct", zap.String("vertex id", vertexID))
93+
return errors.E(Op, fmt.Errorf("internal_init method is absent in struct"))
9594
}
9695

97-
/* Add the dependencies (if) which this vertex needs to init
96+
/* Add the dependencies (if) which this vertex needs to internal_init
9897
Information we know at this step is:
9998
1. vertexID
10099
2. Vertex structure value (interface)
@@ -124,7 +123,7 @@ func (e *Endure) addEdges() error {
124123
}
125124

126125
func (e *Endure) addCollectorsDeps(vertexID string, vertex interface{}) error {
127-
const Op = "add_collectors_deps"
126+
const Op = errors.Op("add_collectors_deps")
128127
if register, ok := vertex.(Collector); ok {
129128
for _, fn := range register.Collects() {
130129
// what type it might depend on?
@@ -167,7 +166,7 @@ func (e *Endure) addCollectorsDeps(vertexID string, vertex interface{}) error {
167166
// vertex - S4 func
168167

169168
// we store pointer in the Deps structure in the isRef field
170-
err = e.graph.AddDep(vertexID, removePointerAsterisk(atStr), structures.Collects, isReference(at), at.Kind())
169+
err = e.graph.AddDep(vertexID, removePointerAsterisk(atStr), Collects, isReference(at), at.Kind())
171170
if err != nil {
172171
return errors.E(Op, err)
173172
}
@@ -177,7 +176,7 @@ func (e *Endure) addCollectorsDeps(vertexID string, vertex interface{}) error {
177176
// get the Vertex from the graph (gVertex)
178177
gVertex := e.graph.GetVertex(vertexID)
179178
if gVertex.Provides == nil {
180-
gVertex.Provides = make(map[string]structures.ProvidedEntry)
179+
gVertex.Provides = make(map[string]ProvidedEntry)
181180
}
182181

183182
if gVertex.Meta.FnsCollectorToInvoke == nil {
@@ -194,7 +193,7 @@ func (e *Endure) addCollectorsDeps(vertexID string, vertex interface{}) error {
194193
}
195194

196195
func (e *Endure) addInitDeps(vertexID string, initMethod reflect.Method) error {
197-
const Op = "add_init_deps"
196+
const Op = errors.Op("add_init_deps")
198197
// Init function in arguments
199198
initArgs := functionParameters(initMethod)
200199

@@ -223,11 +222,11 @@ func (e *Endure) addInitDeps(vertexID string, initMethod reflect.Method) error {
223222
}
224223
}
225224

226-
err := e.graph.AddDep(vertexID, removePointerAsterisk(initArg.String()), structures.Init, isReference(initArg), initArg.Kind())
225+
err := e.graph.AddDep(vertexID, removePointerAsterisk(initArg.String()), Init, isReference(initArg), initArg.Kind())
227226
if err != nil {
228227
return errors.E(Op, err)
229228
}
230-
e.logger.Debug("adding dependency via Init()", zap.String("vertex id", vertexID), zap.String("depends", initArg.String()))
229+
e.logger.Debug("adding dependency via Init()", zap.String("vertex id", vertexID), zap.String("depends on", initArg.String()))
231230
}
232231
return nil
233232
}

common.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package endure
2+
3+
import (
4+
"reflect"
5+
6+
"github.com/cenkalti/backoff/v4"
7+
"github.com/spiral/errors"
8+
"go.uber.org/zap"
9+
)
10+
11+
func (e *Endure) sendResultToUser(res *result) {
12+
e.userResultsCh <- &Result{
13+
Error: res.err,
14+
VertexID: res.vertexID,
15+
}
16+
}
17+
18+
// traverseBackStop used to visit every Prev node and internalStop vertices
19+
func (e *Endure) traverseBackStop(n *DllNode) {
20+
const op = errors.Op("traverse_back_stop")
21+
e.logger.Debug("stopping vertex in the first Serve call", zap.String("vertex id", n.Vertex.ID))
22+
nCopy := n
23+
err := e.shutdown(nCopy, false)
24+
if err != nil {
25+
nCopy.Vertex.SetState(Error)
26+
// ignore errors from internal_stop
27+
e.logger.Error("failed to traverse vertex back", zap.String("vertex id", nCopy.Vertex.ID), zap.Error(errors.E(op, err)))
28+
}
29+
}
30+
31+
func (e *Endure) retryHandler(res *result) {
32+
const op = errors.Op("internal_retry_handler")
33+
// get vertex from the graph
34+
vertex := e.graph.GetVertex(res.vertexID)
35+
if vertex == nil {
36+
e.logger.Error("failed to get vertex from the graph, vertex is nil", zap.String("vertex id from the handleErrorCh channel", res.vertexID))
37+
e.userResultsCh <- &Result{
38+
Error: errors.E(op, errors.Traverse, errors.Str("failed to get vertex from the graph, vertex is nil")),
39+
VertexID: res.vertexID,
40+
}
41+
return
42+
}
43+
44+
// stop without setting Stopped state to the Endure
45+
n := e.runList.Head
46+
err := e.shutdown(n, true)
47+
if err != nil {
48+
e.logger.Error("error happened during shutdown", zap.Error(err))
49+
}
50+
51+
// reset vertex and dependencies to the initial state
52+
// numOfDeps and visited/visiting
53+
vertices := e.graph.Reset(vertex)
54+
55+
// Topologically sort the graph
56+
sorted, err := TopologicalSort(vertices)
57+
if err != nil {
58+
e.logger.Error("error sorting the graph", zap.Error(err))
59+
return
60+
}
61+
if sorted == nil {
62+
e.logger.Error("sorted list should not be nil", zap.String("vertex id from the handleErrorCh channel", res.vertexID))
63+
e.userResultsCh <- &Result{
64+
Error: errors.E(op, errors.Traverse, errors.Str("failed to topologically sort the graph")),
65+
VertexID: res.vertexID,
66+
}
67+
return
68+
}
69+
70+
// Init backoff
71+
b := backoff.NewExponentialBackOff()
72+
b.MaxElapsedTime = e.maxInterval
73+
b.InitialInterval = e.initialInterval
74+
75+
affectedRunList := NewDoublyLinkedList()
76+
for i := 0; i <= len(sorted)-1; i++ {
77+
affectedRunList.Push(sorted[i])
78+
}
79+
80+
// call internal_init
81+
headCopy := affectedRunList.Head
82+
for headCopy != nil {
83+
berr := backoff.Retry(e.backoffInit(headCopy.Vertex), b)
84+
if berr != nil {
85+
e.logger.Error("backoff failed", zap.String("vertex id", headCopy.Vertex.ID), zap.Error(berr))
86+
e.userResultsCh <- &Result{
87+
Error: errors.E(op, errors.FunctionCall, errors.Errorf("error during the Init function call")),
88+
VertexID: headCopy.Vertex.ID,
89+
}
90+
return
91+
}
92+
headCopy = headCopy.Next
93+
}
94+
95+
// call serveInternal
96+
headCopy = affectedRunList.Head
97+
for headCopy != nil {
98+
err := e.serveInternal(headCopy)
99+
if err != nil {
100+
e.userResultsCh <- &Result{
101+
Error: errors.E(op, errors.FunctionCall, errors.Errorf("error during the Serve function call")),
102+
VertexID: headCopy.Vertex.ID,
103+
}
104+
e.logger.Error("fatal error during the serveInternal in the main thread", zap.String("vertex id", headCopy.Vertex.ID), zap.Error(err))
105+
return
106+
}
107+
headCopy = headCopy.Next
108+
}
109+
110+
e.sendResultToUser(res)
111+
}
112+
113+
func (e *Endure) backoffInit(v *Vertex) func() error {
114+
return func() error {
115+
const op = errors.Op("internal_backoff_init")
116+
// we already checked the Interface satisfaction
117+
// at this step absence of Init() is impossible
118+
init, _ := reflect.TypeOf(v.Iface).MethodByName(InitMethodName)
119+
v.SetState(Initializing)
120+
err := e.callInitFn(init, v)
121+
if err != nil {
122+
v.SetState(Error)
123+
e.logger.Error("error occurred during the call INIT function", zap.String("vertex id", v.ID), zap.Error(err))
124+
return errors.E(op, errors.FunctionCall, err)
125+
}
126+
127+
v.SetState(Initialized)
128+
return nil
129+
}
130+
}

container.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,7 @@ type Result struct {
1515
VertexID string
1616
}
1717

18-
type notify struct {
19-
// stop used to notify vertex goroutine, that we need to stop vertex and return from goroutine
20-
stop bool
21-
}
18+
type notify struct{}
2219

2320
type result struct {
2421
// error channel from vertex

doc.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package endure
2+
3+
/*
4+
5+
MIT License
6+
7+
Copyright (c) 2020 Spiral Scout
8+
9+
Permission is hereby granted, free of charge, to any person obtaining a copy
10+
of this software and associated documentation files (the "Software"), to deal
11+
in the Software without restriction, including without limitation the rights
12+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
copies of the Software, and to permit persons to whom the Software is
14+
furnished to do so, subject to the following conditions:
15+
16+
The above copyright notice and this permission notice shall be included in all
17+
copies or substantial portions of the Software.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25+
SOFTWARE.
26+
27+
28+
29+
30+
*/

0 commit comments

Comments
 (0)