From 5c6438d685feb2c0d66fccc0e7c2c6d5abbb4701 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Wed, 28 Oct 2020 15:41:56 +0300 Subject: [PATCH 1/4] Fix bug with partially initialized graph. Rename interface Depender -> Collector, Depends -> Collects --- .github/dependabot.yml | 0 .github/workflows/ci-build.yml | 0 .github/workflows/codeql-analysis.yml | 0 .gitignore | 0 .golangci.yml | 0 .vscode/launch.json | 17 ++++ Makefile | 0 README.md | 8 +- bors.toml | 0 calculate_deps.go | 28 ++--- container.go | 11 +- endure.go | 11 +- examples/README.md | 0 examples/sample_1/.gitignore | 0 examples/sample_1/README.md | 0 examples/sample_1/go.mod | 0 examples/sample_1/go.sum | 0 examples/sample_1/graph.png | Bin examples/sample_1/main.go | 0 examples/sample_1/modules/db/db_layer.go | 0 examples/sample_1/modules/gzip/gzip.go | 0 examples/sample_1/modules/headers/headers.go | 0 examples/sample_1/modules/http/http_layer.go | 3 +- examples/sample_1/modules/logger/logger.go | 0 go.mod | 0 go.sum | 0 images/graph.png | Bin images/happyPathGraph.png | Bin internal.go | 96 +++++++++++------- reflect.go | 0 structures/graph.go | 28 ++--- structures/linked_list.go | 0 structures/visualize_graph.go | 0 structures/visualize_graph_windows.go | 0 tests/backoff/backoff_test.go | 0 tests/backoff/plugins/plugin1/plugin1.go | 0 tests/backoff/plugins/plugin2/plugin2.go | 0 tests/backoff/plugins/plugin3/plugin3.go | 6 +- tests/backoff/plugins/plugin4/plugin4.go | 0 .../disabled_vertices_test.go | 0 tests/disabled_vertices/graph.png | Bin tests/disabled_vertices/plugin1/plugin1.go | 0 tests/disabled_vertices/plugin2/plugin2.go | 0 tests/disabled_vertices/plugin3/plugin3.go | 0 tests/disabled_vertices/plugin4/plugin4.go | 0 tests/disabled_vertices/plugin5/plugin5.go | 0 tests/disabled_vertices/plugin6/plugin6.go | 0 tests/disabled_vertices/plugin7/plugin7.go | 0 tests/disabled_vertices/plugin8/plugin8.go | 0 tests/disabled_vertices/plugin9/plugin9.go | 0 tests/happy_scenarios/README.md | 0 tests/happy_scenarios/graph.png | Bin tests/happy_scenarios/happyScenario_test.go | 0 tests/happy_scenarios/plugin1/plugin1.go | 4 +- tests/happy_scenarios/plugin2/plugin2.go | 0 tests/happy_scenarios/plugin3/plugin3.go | 4 +- tests/happy_scenarios/plugin4/plugin4.go | 2 +- tests/happy_scenarios/plugin5/plugin5.go | 0 tests/happy_scenarios/plugin6/plugin6.go | 0 tests/happy_scenarios/plugin7/plugin7.go | 0 tests/happy_scenarios/plugin8/plugin8.go | 2 +- .../plugin1/foo2_value.go | 0 .../plugin2/foo4_value.go | 0 tests/interfaces/graph.png | Bin 0 -> 95 bytes tests/interfaces/interfaces_test.go | 2 +- .../named/randominterface/plugin1.go | 0 .../named/randominterface/plugin2.go | 0 tests/interfaces/named/registers/plugin1.go | 0 tests/interfaces/named/registers/plugin2.go | 0 .../interfaces/named/registersfail/plugin1.go | 0 .../interfaces/named/registersfail/plugin2.go | 0 tests/interfaces/plugins/plugin1/plugin1.go | 0 tests/interfaces/plugins/plugin2/plugin2.go | 0 tests/interfaces/plugins/plugin3/plugin3.go | 0 tests/interfaces/plugins/plugin4/plugin4.go | 0 tests/interfaces/plugins/plugin5/plugin5.go | 2 +- .../plugin1/plugin1.go | 0 .../plugin2/plugin2.go | 0 tests/issues/issue33/issue33.go | 2 +- tests/issues/issues_test.go | 0 .../foo.go | 6 +- tests/stress/InitErr/foo1_init_err.go | 0 tests/stress/InitErr/foo2_init_err.go | 0 tests/stress/README.md | 0 tests/stress/ServeErr/foo1_serve_error.go | 0 tests/stress/ServeErr/foo3_serve_error.go | 4 +- tests/stress/ServeErr/foo4ServeError.go | 4 +- tests/stress/ServeErr/plugin2.go | 0 tests/stress/ServeErr/plugin5.go | 0 tests/stress/ServeRetryErr/plugin1.go | 0 tests/stress/ServeRetryErr/plugin2.go | 0 tests/stress/ServeRetryErr/plugin3.go | 8 +- tests/stress/ServeRetryErr/plugin4.go | 0 tests/stress/ServeRetryErr/plugin5.go | 0 tests/stress/ServeRetryErr/plugin6.go | 0 tests/stress/stress_test.go | 28 ++--- utils.go | 0 97 files changed, 155 insertions(+), 121 deletions(-) mode change 100644 => 100755 .github/dependabot.yml mode change 100644 => 100755 .github/workflows/ci-build.yml mode change 100644 => 100755 .github/workflows/codeql-analysis.yml mode change 100644 => 100755 .gitignore mode change 100644 => 100755 .golangci.yml create mode 100755 .vscode/launch.json mode change 100644 => 100755 Makefile mode change 100644 => 100755 README.md mode change 100644 => 100755 bors.toml mode change 100644 => 100755 calculate_deps.go mode change 100644 => 100755 container.go mode change 100644 => 100755 endure.go mode change 100644 => 100755 examples/README.md mode change 100644 => 100755 examples/sample_1/.gitignore mode change 100644 => 100755 examples/sample_1/README.md mode change 100644 => 100755 examples/sample_1/go.mod mode change 100644 => 100755 examples/sample_1/go.sum mode change 100644 => 100755 examples/sample_1/graph.png mode change 100644 => 100755 examples/sample_1/main.go mode change 100644 => 100755 examples/sample_1/modules/db/db_layer.go mode change 100644 => 100755 examples/sample_1/modules/gzip/gzip.go mode change 100644 => 100755 examples/sample_1/modules/headers/headers.go mode change 100644 => 100755 examples/sample_1/modules/http/http_layer.go mode change 100644 => 100755 examples/sample_1/modules/logger/logger.go mode change 100644 => 100755 go.mod mode change 100644 => 100755 go.sum mode change 100644 => 100755 images/graph.png mode change 100644 => 100755 images/happyPathGraph.png mode change 100644 => 100755 internal.go mode change 100644 => 100755 reflect.go mode change 100644 => 100755 structures/graph.go mode change 100644 => 100755 structures/linked_list.go mode change 100644 => 100755 structures/visualize_graph.go mode change 100644 => 100755 structures/visualize_graph_windows.go mode change 100644 => 100755 tests/backoff/backoff_test.go mode change 100644 => 100755 tests/backoff/plugins/plugin1/plugin1.go mode change 100644 => 100755 tests/backoff/plugins/plugin2/plugin2.go mode change 100644 => 100755 tests/backoff/plugins/plugin3/plugin3.go mode change 100644 => 100755 tests/backoff/plugins/plugin4/plugin4.go mode change 100644 => 100755 tests/disabled_vertices/disabled_vertices_test.go mode change 100644 => 100755 tests/disabled_vertices/graph.png mode change 100644 => 100755 tests/disabled_vertices/plugin1/plugin1.go mode change 100644 => 100755 tests/disabled_vertices/plugin2/plugin2.go mode change 100644 => 100755 tests/disabled_vertices/plugin3/plugin3.go mode change 100644 => 100755 tests/disabled_vertices/plugin4/plugin4.go mode change 100644 => 100755 tests/disabled_vertices/plugin5/plugin5.go mode change 100644 => 100755 tests/disabled_vertices/plugin6/plugin6.go mode change 100644 => 100755 tests/disabled_vertices/plugin7/plugin7.go mode change 100644 => 100755 tests/disabled_vertices/plugin8/plugin8.go mode change 100644 => 100755 tests/disabled_vertices/plugin9/plugin9.go mode change 100644 => 100755 tests/happy_scenarios/README.md mode change 100644 => 100755 tests/happy_scenarios/graph.png mode change 100644 => 100755 tests/happy_scenarios/happyScenario_test.go mode change 100644 => 100755 tests/happy_scenarios/plugin1/plugin1.go mode change 100644 => 100755 tests/happy_scenarios/plugin2/plugin2.go mode change 100644 => 100755 tests/happy_scenarios/plugin3/plugin3.go mode change 100644 => 100755 tests/happy_scenarios/plugin4/plugin4.go mode change 100644 => 100755 tests/happy_scenarios/plugin5/plugin5.go mode change 100644 => 100755 tests/happy_scenarios/plugin6/plugin6.go mode change 100644 => 100755 tests/happy_scenarios/plugin7/plugin7.go mode change 100644 => 100755 tests/happy_scenarios/plugin8/plugin8.go mode change 100644 => 100755 tests/happy_scenarios/provided_value_but_need_pointer/plugin1/foo2_value.go mode change 100644 => 100755 tests/happy_scenarios/provided_value_but_need_pointer/plugin2/foo4_value.go create mode 100755 tests/interfaces/graph.png mode change 100644 => 100755 tests/interfaces/interfaces_test.go mode change 100644 => 100755 tests/interfaces/named/randominterface/plugin1.go mode change 100644 => 100755 tests/interfaces/named/randominterface/plugin2.go mode change 100644 => 100755 tests/interfaces/named/registers/plugin1.go mode change 100644 => 100755 tests/interfaces/named/registers/plugin2.go mode change 100644 => 100755 tests/interfaces/named/registersfail/plugin1.go mode change 100644 => 100755 tests/interfaces/named/registersfail/plugin2.go mode change 100644 => 100755 tests/interfaces/plugins/plugin1/plugin1.go mode change 100644 => 100755 tests/interfaces/plugins/plugin2/plugin2.go mode change 100644 => 100755 tests/interfaces/plugins/plugin3/plugin3.go mode change 100644 => 100755 tests/interfaces/plugins/plugin4/plugin4.go mode change 100644 => 100755 tests/interfaces/plugins/plugin5/plugin5.go mode change 100644 => 100755 tests/interfaces/service/not_implemented_service/plugin1/plugin1.go mode change 100644 => 100755 tests/interfaces/service/not_implemented_service/plugin2/plugin2.go mode change 100644 => 100755 tests/issues/issue33/issue33.go mode change 100644 => 100755 tests/issues/issues_test.go rename tests/stress/{DependerFuncReturn => CollectorFuncReturn}/foo.go (81%) mode change 100644 => 100755 mode change 100644 => 100755 tests/stress/InitErr/foo1_init_err.go mode change 100644 => 100755 tests/stress/InitErr/foo2_init_err.go mode change 100644 => 100755 tests/stress/README.md mode change 100644 => 100755 tests/stress/ServeErr/foo1_serve_error.go mode change 100644 => 100755 tests/stress/ServeErr/foo3_serve_error.go mode change 100644 => 100755 tests/stress/ServeErr/foo4ServeError.go mode change 100644 => 100755 tests/stress/ServeErr/plugin2.go mode change 100644 => 100755 tests/stress/ServeErr/plugin5.go mode change 100644 => 100755 tests/stress/ServeRetryErr/plugin1.go mode change 100644 => 100755 tests/stress/ServeRetryErr/plugin2.go mode change 100644 => 100755 tests/stress/ServeRetryErr/plugin3.go mode change 100644 => 100755 tests/stress/ServeRetryErr/plugin4.go mode change 100644 => 100755 tests/stress/ServeRetryErr/plugin5.go mode change 100644 => 100755 tests/stress/ServeRetryErr/plugin6.go mode change 100644 => 100755 tests/stress/stress_test.go mode change 100644 => 100755 utils.go diff --git a/.github/dependabot.yml b/.github/dependabot.yml old mode 100644 new mode 100755 diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml old mode 100644 new mode 100755 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml old mode 100644 new mode 100755 diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/.golangci.yml b/.golangci.yml old mode 100644 new mode 100755 diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100755 index 0000000..f5bfb2e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch", + "type": "godlvdap", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}", + "env": {}, + "args": [] + } + ] +} \ No newline at end of file diff --git a/Makefile b/Makefile old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 3712438..db90a18 --- a/README.md +++ b/README.md @@ -82,9 +82,9 @@ type ( Provides() []interface{} } - // Depender declares the ability to accept the plugins which match the provided method signature. - Depender interface { - Depends() []interface{} + // Collector declares the ability to accept the plugins which match the provided method signature. + Collector interface { + Collects() []interface{} } ) ``` @@ -92,5 +92,5 @@ Order is the following: 1. `Init() error` - is mandatory to implement. In your structure (which you pass to Endure), you should have this method as a receiver. It can accept as parameter any passed to the `Endure` structure (see samples) or interface (with limitations). 3. `Service` - is optional to implement. It has 2 main methods - `Serve` which should run the plugin and return initialized golang channel with errors, and `Stop` to shut down the plugin. 4. `Provider` - is optional to implement. It is used to provide some dependency if you need to extend your struct. -5. `Depender` - is optional to implement. It is used to mark a structure (vertex) as some struct dependency. It can accept interfaces which implement the caller. +5. `Collector` - is optional to implement. It is used to mark a structure (vertex) as some struct dependency. It can accept interfaces which implement the caller. 6. `Named` - is optional to implement. This is a special kind of interface which provides the name of the struct (plugin, vertex) to the caller. Is useful in logger (for example) to know user-friendly plugin name. diff --git a/bors.toml b/bors.toml old mode 100644 new mode 100755 diff --git a/calculate_deps.go b/calculate_deps.go old mode 100644 new mode 100755 index d80e7ae..1b259e8 --- a/calculate_deps.go +++ b/calculate_deps.go @@ -90,7 +90,7 @@ func (e *Endure) addEdges() error { init, _ := reflect.TypeOf(vrtx.Iface).MethodByName(InitMethodName) if init.Type == nil { - e.logger.Fatal("init method is absent in struct", zap.String("vertexId", vertexID)) + e.logger.Fatal("init method is absent in struct", zap.String("vertex id", vertexID)) return errors.E(Op, fmt.Errorf("init method is absent in struct")) } @@ -103,13 +103,13 @@ func (e *Endure) addEdges() error { 5. FunctionName of the dependencies which we should found We add 3 and 4 points to the Vertex */ - err := e.addDependersDeps(vertexID, vrtx.Iface) + err := e.addCollectorsDeps(vertexID, vrtx.Iface) if err != nil { return errors.E(Op, err) } /* - At this step we know (and build) all dependencies via the Depends interface and connected all providers + At this step we know (and build) all dependencies via the Collects interface and connected all providers to it's dependencies. The next step is to calculate dependencies provided by the Init() method for example S1.Init(foo2.DB) S1 --> foo2.S2 (not foo2.DB, because vertex which provides foo2.DB is foo2.S2) @@ -123,10 +123,10 @@ func (e *Endure) addEdges() error { return nil } -func (e *Endure) addDependersDeps(vertexID string, vertex interface{}) error { - const Op = "add_dependers_deps" - if register, ok := vertex.(Depender); ok { - for _, fn := range register.Depends() { +func (e *Endure) addCollectorsDeps(vertexID string, vertex interface{}) error { + const Op = "add_collectors_deps" + if register, ok := vertex.(Collector); ok { + for _, fn := range register.Collects() { // what type it might depend on? argsTypes, err := argType(fn) if err != nil { @@ -144,7 +144,7 @@ func (e *Endure) addDependersDeps(vertexID string, vertex interface{}) error { if vertexID == atStr { continue } - // depends at interface via Dependers + // depends at interface via Collectors /* In this case we should do the following: 1. Find all types, which implement this interface @@ -167,11 +167,11 @@ func (e *Endure) addDependersDeps(vertexID string, vertex interface{}) error { // vertex - S4 func // we store pointer in the Deps structure in the isRef field - err = e.graph.AddDep(vertexID, removePointerAsterisk(atStr), structures.Depends, isReference(at), at.Kind()) + err = e.graph.AddDep(vertexID, removePointerAsterisk(atStr), structures.Collects, isReference(at), at.Kind()) if err != nil { return errors.E(Op, err) } - e.logger.Debug("adding dependency via Depends()", zap.String("vertex id", vertexID), zap.String("depends", atStr)) + e.logger.Debug("adding dependency via Collects()", zap.String("vertex id", vertexID), zap.String("depends", atStr)) } // get the Vertex from the graph (gVertex) @@ -180,13 +180,13 @@ func (e *Endure) addDependersDeps(vertexID string, vertex interface{}) error { gVertex.Provides = make(map[string]structures.ProvidedEntry) } - if gVertex.Meta.FnsDependerToInvoke == nil { - gVertex.Meta.FnsDependerToInvoke = make([]string, 0, 5) + if gVertex.Meta.FnsCollectorToInvoke == nil { + gVertex.Meta.FnsCollectorToInvoke = make([]string, 0, 5) } - e.logger.Debug("appending depender function to invoke later", zap.String("vertex id", vertexID), zap.String("function name", getFunctionName(fn))) + e.logger.Debug("appending collector function to invoke later", zap.String("vertex id", vertexID), zap.String("function name", getFunctionName(fn))) - gVertex.Meta.FnsDependerToInvoke = append(gVertex.Meta.FnsDependerToInvoke, getFunctionName(fn)) + gVertex.Meta.FnsCollectorToInvoke = append(gVertex.Meta.FnsCollectorToInvoke, getFunctionName(fn)) } } diff --git a/container.go b/container.go old mode 100644 new mode 100755 index 36f1a22..3334723 --- a/container.go +++ b/container.go @@ -3,12 +3,13 @@ package endure // InitMethodName is the function name for the reflection const InitMethodName = "Init" -// ServeMethodName +// ServeMethodName is the function name for the Serve const ServeMethodName = "Serve" -// Stop is the function name for the reflection to Stop the service +// StopMethodName is the function name for the reflection to Stop the service const StopMethodName = "Stop" +// Result is the information which endure send to the user type Result struct { Error error VertexID string @@ -57,8 +58,8 @@ type ( Provides() []interface{} } - // Depender declares the ability to accept the plugins which match the provided method signature. - Depender interface { - Depends() []interface{} + // Collector declares the ability to accept the plugins which match the provided method signature. + Collector interface { + Collects() []interface{} } ) diff --git a/endure.go b/endure.go old mode 100644 new mode 100755 index bc783b6..682c318 --- a/endure.go +++ b/endure.go @@ -176,7 +176,7 @@ func Visualize(print bool) Options { } } -// Depender depends the dependencies +// Collector depends the dependencies // name is a name of the dependency, for example - S2 // vertex is a value -> pointer to the structure func (e *Endure) Register(vertex interface{}) error { @@ -188,7 +188,7 @@ func (e *Endure) Register(vertex interface{}) error { return errors.E(op, errors.Register, errors.Errorf("you should pass pointer to the structure instead of value")) } - /* Depender the type + /* Collector the type Information we know at this step is: 1. vertexID 2. Vertex structure value (interface) @@ -265,7 +265,12 @@ func (e *Endure) Init() error { return nil } +// Serve starts serving the graph +// This is the initial serve, if error produced immedeately in the initial serve, endure will traverse deps back, call stop and exit func (e *Endure) Serve() (<-chan *Result, error) { + e.mutex.Lock() + defer e.mutex.Unlock() + const op = errors.Op("Serve") e.startMainThread() @@ -281,6 +286,7 @@ func (e *Endure) Serve() (<-chan *Result, error) { atLeastOne = true err := e.serve(nCopy) if err != nil { + e.traverseBackStop(nCopy) return nil, errors.E(op, errors.Serve, err) } nCopy = nCopy.Next @@ -292,6 +298,7 @@ func (e *Endure) Serve() (<-chan *Result, error) { return e.userResultsCh, nil } +// Stop stops the execution and call Stop on every vertex func (e *Endure) Stop() error { e.mutex.Lock() defer e.mutex.Unlock() diff --git a/examples/README.md b/examples/README.md old mode 100644 new mode 100755 diff --git a/examples/sample_1/.gitignore b/examples/sample_1/.gitignore old mode 100644 new mode 100755 diff --git a/examples/sample_1/README.md b/examples/sample_1/README.md old mode 100644 new mode 100755 diff --git a/examples/sample_1/go.mod b/examples/sample_1/go.mod old mode 100644 new mode 100755 diff --git a/examples/sample_1/go.sum b/examples/sample_1/go.sum old mode 100644 new mode 100755 diff --git a/examples/sample_1/graph.png b/examples/sample_1/graph.png old mode 100644 new mode 100755 diff --git a/examples/sample_1/main.go b/examples/sample_1/main.go old mode 100644 new mode 100755 diff --git a/examples/sample_1/modules/db/db_layer.go b/examples/sample_1/modules/db/db_layer.go old mode 100644 new mode 100755 diff --git a/examples/sample_1/modules/gzip/gzip.go b/examples/sample_1/modules/gzip/gzip.go old mode 100644 new mode 100755 diff --git a/examples/sample_1/modules/headers/headers.go b/examples/sample_1/modules/headers/headers.go old mode 100644 new mode 100755 diff --git a/examples/sample_1/modules/http/http_layer.go b/examples/sample_1/modules/http/http_layer.go old mode 100644 new mode 100755 index cbb725b..67404b3 --- a/examples/sample_1/modules/http/http_layer.go +++ b/examples/sample_1/modules/http/http_layer.go @@ -100,7 +100,7 @@ func (h *Http) Stop() error { return nil } -func (h *Http) Depends() []interface{} { +func (h *Http) Collects() []interface{} { return []interface{}{ h.AddMiddleware, } @@ -111,7 +111,6 @@ func (h *Http) AddMiddleware(m Middleware) error { return nil } - ///////////////// INFRA HANDLERS ////////////////////////////// func (h *Http) update(writer http.ResponseWriter, request *http.Request) { diff --git a/examples/sample_1/modules/logger/logger.go b/examples/sample_1/modules/logger/logger.go old mode 100644 new mode 100755 diff --git a/go.mod b/go.mod old mode 100644 new mode 100755 diff --git a/go.sum b/go.sum old mode 100644 new mode 100755 diff --git a/images/graph.png b/images/graph.png old mode 100644 new mode 100755 diff --git a/images/happyPathGraph.png b/images/happyPathGraph.png old mode 100644 new mode 100755 diff --git a/internal.go b/internal.go old mode 100644 new mode 100755 index 3467ef8..1f4ed2f --- a/internal.go +++ b/internal.go @@ -92,17 +92,17 @@ func (e *Endure) callInitFn(init reflect.Method, vertex *structures.Vertex) erro return errors.E(op, errors.ArgType, errors.Str("0 or less parameters for Init")) } - if len(vertex.Meta.DependsDepsToInvoke) > 0 { - for i := 0; i < len(vertex.Meta.DependsDepsToInvoke); i++ { + if len(vertex.Meta.CollectsDepsToInvoke) > 0 { + for i := 0; i < len(vertex.Meta.CollectsDepsToInvoke); i++ { // Interface dependency - if vertex.Meta.DependsDepsToInvoke[i].Kind == reflect.Interface { - err = e.traverseCallDependersInterface(vertex) + if vertex.Meta.CollectsDepsToInvoke[i].Kind == reflect.Interface { + err = e.traverseCallCollectorsInterface(vertex) if err != nil { return errors.E(op, errors.Traverse, err) } } else { // structure dependence - err = e.traverseCallDependers(vertex) + err = e.traverseCallCollectors(vertex) if err != nil { return errors.E(op, errors.Traverse, err) } @@ -112,11 +112,11 @@ func (e *Endure) callInitFn(init reflect.Method, vertex *structures.Vertex) erro return nil } -func (e *Endure) traverseCallDependersInterface(vertex *structures.Vertex) error { - const op = errors.Op("internal_traverse_call_dependers_interface") - for i := 0; i < len(vertex.Meta.DependsDepsToInvoke); i++ { +func (e *Endure) traverseCallCollectorsInterface(vertex *structures.Vertex) error { + const op = errors.Op("internal_traverse_call_collectors_interface") + for i := 0; i < len(vertex.Meta.CollectsDepsToInvoke); i++ { // get dependency id (vertex id) - depID := vertex.Meta.DependsDepsToInvoke[i].Name + depID := vertex.Meta.CollectsDepsToInvoke[i].Name // find vertex which provides dependency providers := e.graph.FindProviders(depID) @@ -143,7 +143,7 @@ func (e *Endure) traverseCallDependersInterface(vertex *structures.Vertex) error // if type provides needed type // value - reference and init dep also reference switch { - case *vertexVal.IsReference == *vertex.Meta.DependsDepsToInvoke[i].IsReference: + case *vertexVal.IsReference == *vertex.Meta.CollectsDepsToInvoke[i].IsReference: inInterface = append(inInterface, *vertexVal.Value) case *vertexVal.IsReference: // same type, but difference in the refs @@ -164,7 +164,7 @@ func (e *Endure) traverseCallDependersInterface(vertex *structures.Vertex) error } } - err := e.callDependerFns(vertex, inInterface) + err := e.callCollectorFns(vertex, inInterface) if err != nil { return errors.E(op, errors.Traverse, err) } @@ -175,15 +175,15 @@ func (e *Endure) traverseCallDependersInterface(vertex *structures.Vertex) error return nil } -func (e *Endure) traverseCallDependers(vertex *structures.Vertex) error { - const op = "internal_traverse_call_dependers" +func (e *Endure) traverseCallCollectors(vertex *structures.Vertex) error { + const op = "internal_traverse_call_collectors" in := make([]reflect.Value, 0, 2) // add service itself in = append(in, reflect.ValueOf(vertex.Iface)) - for i := 0; i < len(vertex.Meta.DependsDepsToInvoke); i++ { + for i := 0; i < len(vertex.Meta.CollectsDepsToInvoke); i++ { // get dependency id (vertex id) - depID := vertex.Meta.DependsDepsToInvoke[i].Name + depID := vertex.Meta.CollectsDepsToInvoke[i].Name // find vertex which provides dependency providers := e.graph.FindProviders(depID) // search for providers @@ -192,7 +192,7 @@ func (e *Endure) traverseCallDependers(vertex *structures.Vertex) error { // if type provides needed type if vertexID == depID { switch { - case *val.IsReference == *vertex.Meta.DependsDepsToInvoke[i].IsReference: + case *val.IsReference == *vertex.Meta.CollectsDepsToInvoke[i].IsReference: in = append(in, *val.Value) case *val.IsReference: // same type, but difference in the refs @@ -217,7 +217,7 @@ func (e *Endure) traverseCallDependers(vertex *structures.Vertex) error { } } - err := e.callDependerFns(vertex, in) + err := e.callCollectorFns(vertex, in) if err != nil { return errors.E(op, errors.Traverse, err) } @@ -225,17 +225,17 @@ func (e *Endure) traverseCallDependers(vertex *structures.Vertex) error { return nil } -func (e *Endure) callDependerFns(vertex *structures.Vertex, in []reflect.Value) error { - const op = errors.Op("internal_call_depender_functions") - // type implements Depender interface - if reflect.TypeOf(vertex.Iface).Implements(reflect.TypeOf((*Depender)(nil)).Elem()) { - // if type implements Depender() it should has FnsProviderToInvoke - if vertex.Meta.DependsDepsToInvoke != nil { - for k := 0; k < len(vertex.Meta.FnsDependerToInvoke); k++ { - m, ok := reflect.TypeOf(vertex.Iface).MethodByName(vertex.Meta.FnsDependerToInvoke[k]) +func (e *Endure) callCollectorFns(vertex *structures.Vertex, in []reflect.Value) error { + const op = errors.Op("internal_call_collector_functions") + // type implements Collector interface + if reflect.TypeOf(vertex.Iface).Implements(reflect.TypeOf((*Collector)(nil)).Elem()) { + // if type implements Collector() it should has FnsProviderToInvoke + if vertex.Meta.CollectsDepsToInvoke != nil { + for k := 0; k < len(vertex.Meta.FnsCollectorToInvoke); k++ { + m, ok := reflect.TypeOf(vertex.Iface).MethodByName(vertex.Meta.FnsCollectorToInvoke[k]) if !ok { - e.logger.Error("type has missing method in FnsDependerToInvoke", zap.String("vertex id", vertex.ID), zap.String("method", vertex.Meta.FnsDependerToInvoke[k])) - return errors.E(op, errors.FunctionCall, errors.Str("type has missing method in FnsDependerToInvoke")) + e.logger.Error("type has missing method in FnsCollectorToInvoke", zap.String("vertex id", vertex.ID), zap.String("method", vertex.Meta.FnsCollectorToInvoke[k])) + return errors.E(op, errors.FunctionCall, errors.Str("type has missing method in FnsCollectorToInvoke")) } ret := m.Func.Call(in) @@ -245,13 +245,13 @@ func (e *Endure) callDependerFns(vertex *structures.Vertex, in []reflect.Value) rErr := ret[len(ret)-1].Interface() if rErr != nil { if err, ok := rErr.(error); ok && e != nil { - e.logger.Error("error calling DependerFns", zap.String("vertex id", vertex.ID), zap.Error(err)) + e.logger.Error("error calling CollectorFns", zap.String("vertex id", vertex.ID), zap.Error(err)) return errors.E(op, errors.FunctionCall, err) } return errors.E(op, errors.FunctionCall, errors.Str("unknown error occurred during the function call")) } } else { - return errors.E(op, errors.FunctionCall, errors.Str("depender should return Value and error types")) + return errors.E(op, errors.FunctionCall, errors.Str("collector should return Value and error types")) } } } @@ -422,23 +422,28 @@ Algorithm is the following (all steps executing in the topological order): */ // call configure on the node -func (e *Endure) callServeFn(vertex *structures.Vertex, in []reflect.Value) *result { +func (e *Endure) callServeFn(vertex *structures.Vertex, in []reflect.Value) (*result, error) { + const op = errors.Op("call_serve_fn") m, _ := reflect.TypeOf(vertex.Iface).MethodByName(ServeMethodName) ret := m.Func.Call(in) res := ret[0].Interface() if res != nil { - e.logger.Debug("start serving vertex", zap.String("vertexId", vertex.ID)) + e.logger.Debug("start serving vertex", zap.String("vertex id", vertex.ID)) if e, ok := res.(chan error); ok && e != nil { + // error come righth after we start serving the vertex + if len(e) > 0 { + return nil, errors.E(op, errors.FunctionCall, errors.Str("got immediate error from vertex, stopping serve execution")) + } return &result{ errCh: e, signal: make(chan notify), vertexID: vertex.ID, - } + }, nil } } // error, result should not be nil // the only one reason to be nil is to vertex return parameter (channel) is not initialized - return nil + return nil, nil } func (e *Endure) stop(vID string) error { @@ -462,7 +467,7 @@ func (e *Endure) stop(vID string) error { func (e *Endure) callStopFn(vertex *structures.Vertex, in []reflect.Value) error { const op = errors.Op("internal_call_stop_function") // Call Stop() method, which returns only error (or nil) - e.logger.Debug("stopping vertex", zap.String("vertexId", vertex.ID)) + e.logger.Debug("calling stop function on the vertex", zap.String("vertex id", vertex.ID)) m, _ := reflect.TypeOf(vertex.Iface).MethodByName(StopMethodName) ret := m.Func.Call(in) rErr := ret[0].Interface() @@ -564,7 +569,10 @@ func (e *Endure) serve(n *structures.DllNode) error { // add service itself in = append(in, reflect.ValueOf(n.Vertex.Iface)) - res := e.callServeFn(n.Vertex, in) + res, err := e.callServeFn(n.Vertex, in) + if err != nil { + return errors.E(op, errors.FunctionCall, err) + } if res != nil { e.results.Store(res.vertexID, res) } else { @@ -579,6 +587,22 @@ func (e *Endure) serve(n *structures.DllNode) error { return nil } +// traverseBackStop used to visit every Prev node and stop vertices +func (e *Endure) traverseBackStop(n *structures.DllNode) error { + const op = errors.Op("traverse_back_stop") + e.logger.Debug("stopping vertex in the first Serve call", zap.String("vertex id", n.Vertex.ID)) + nCopy := n + for nCopy != nil { + err := e.stop(nCopy.Vertex.ID) + if err != nil { + // ignore errors from stop + e.logger.Error("failed to traverse vertex back", zap.String("vertex id", nCopy.Vertex.ID), zap.Error(errors.E(op, err))) + } + nCopy = nCopy.Prev + } + return nil +} + func (e *Endure) startMainThread() { /* Main thread is the main Endure unit of work @@ -674,7 +698,7 @@ func (e *Endure) retryHandler(res *result) { // call serve headCopy = affectedRunList.Head for headCopy != nil { - err := e.serve(headCopy) + err = e.serve(headCopy) if err != nil { e.userResultsCh <- &Result{ Error: errors.E(op, errors.FunctionCall, errors.Errorf("error during the Serve function call")), diff --git a/reflect.go b/reflect.go old mode 100644 new mode 100755 diff --git a/structures/graph.go b/structures/graph.go old mode 100644 new mode 100755 index 3f99a6c..ff5cfd4 --- a/structures/graph.go +++ b/structures/graph.go @@ -10,7 +10,7 @@ type Kind int const ( Init Kind = iota - Depends + Collects ) type disabler interface { @@ -36,16 +36,16 @@ type Meta struct { Order int // FnsProviderToInvoke is the function names to invoke if type implements Provides() interface FnsProviderToInvoke []ProviderEntry - // FnsDependerToInvoke is the function names to invoke if type implements Depender() interface - FnsDependerToInvoke []string + // FnsCollectorToInvoke is the function names to invoke if type implements Collector() interface + FnsCollectorToInvoke []string // List of the vertex deps // foo4.DB, foo4.S4 etc.. which were found in the Init() method InitDepsToInvoke []Entry // List of the vertex deps - // foo4.DB, foo4.S4 etc.. which were found in the Depends() method - DependsDepsToInvoke []Entry + // foo4.DB, foo4.S4 etc.. which were found in the Collects() method + CollectsDepsToInvoke []Entry } type ProviderEntry struct { @@ -183,7 +183,7 @@ func (g *Graph) addInterfaceDep(vertexID, depID string, method Kind, isRef bool) // to call later // because we should know Init method parameters for every Vertex // for example, we should know http.Middleware dependency and later invoke all types which it implement - // OR know Depends methods to invoke + // OR know Collects methods to invoke g.addToList(method, vertex, depID, isRef, depVertices[i].ID, reflect.Interface) // append depID vertex @@ -200,7 +200,7 @@ func (g *Graph) addInterfaceDep(vertexID, depID string, method Kind, isRef bool) return nil } -// Add meta information to the InitDepsToInvoke or DependsDepsToInvoke +// Add meta information to the InitDepsToInvoke or CollectsDepsToInvoke func (g *Graph) addToList(method Kind, vertex *Vertex, depID string, isRef bool, refId string, kind reflect.Kind) { switch method { case Init: @@ -213,23 +213,23 @@ func (g *Graph) addToList(method Kind, vertex *Vertex, depID string, isRef bool, IsReference: &isRef, Kind: kind, }) - case Depends: - if vertex.Meta.DependsDepsToInvoke == nil { - vertex.Meta.DependsDepsToInvoke = make([]Entry, 0, 1) - vertex.Meta.DependsDepsToInvoke = append(vertex.Meta.DependsDepsToInvoke, Entry{ + case Collects: + if vertex.Meta.CollectsDepsToInvoke == nil { + vertex.Meta.CollectsDepsToInvoke = make([]Entry, 0, 1) + vertex.Meta.CollectsDepsToInvoke = append(vertex.Meta.CollectsDepsToInvoke, Entry{ RefId: refId, Name: depID, IsReference: &isRef, Kind: kind, }) } else { - // search if DependsDepsToInvoke already contains interface dep - for _, v := range vertex.Meta.DependsDepsToInvoke { + // search if CollectsDepsToInvoke already contains interface dep + for _, v := range vertex.Meta.CollectsDepsToInvoke { if v.Name == depID { continue } - vertex.Meta.DependsDepsToInvoke = append(vertex.Meta.DependsDepsToInvoke, Entry{ + vertex.Meta.CollectsDepsToInvoke = append(vertex.Meta.CollectsDepsToInvoke, Entry{ RefId: refId, Name: depID, IsReference: &isRef, diff --git a/structures/linked_list.go b/structures/linked_list.go old mode 100644 new mode 100755 diff --git a/structures/visualize_graph.go b/structures/visualize_graph.go old mode 100644 new mode 100755 diff --git a/structures/visualize_graph_windows.go b/structures/visualize_graph_windows.go old mode 100644 new mode 100755 diff --git a/tests/backoff/backoff_test.go b/tests/backoff/backoff_test.go old mode 100644 new mode 100755 diff --git a/tests/backoff/plugins/plugin1/plugin1.go b/tests/backoff/plugins/plugin1/plugin1.go old mode 100644 new mode 100755 diff --git a/tests/backoff/plugins/plugin2/plugin2.go b/tests/backoff/plugins/plugin2/plugin2.go old mode 100644 new mode 100755 diff --git a/tests/backoff/plugins/plugin3/plugin3.go b/tests/backoff/plugins/plugin3/plugin3.go old mode 100644 new mode 100755 index cc440f7..34f8760 --- a/tests/backoff/plugins/plugin3/plugin3.go +++ b/tests/backoff/plugins/plugin3/plugin3.go @@ -1,6 +1,9 @@ package plugin3 -import "errors" +import ( + "errors" + "time" +) var number2 int = 0 @@ -18,6 +21,7 @@ func (f *Plugin3) Serve() chan error { errCh := make(chan error, 1) number2 += 1 go func() { + time.Sleep(time.Millisecond * 500) errCh <- errors.New("test error2") }() return errCh diff --git a/tests/backoff/plugins/plugin4/plugin4.go b/tests/backoff/plugins/plugin4/plugin4.go old mode 100644 new mode 100755 diff --git a/tests/disabled_vertices/disabled_vertices_test.go b/tests/disabled_vertices/disabled_vertices_test.go old mode 100644 new mode 100755 diff --git a/tests/disabled_vertices/graph.png b/tests/disabled_vertices/graph.png old mode 100644 new mode 100755 diff --git a/tests/disabled_vertices/plugin1/plugin1.go b/tests/disabled_vertices/plugin1/plugin1.go old mode 100644 new mode 100755 diff --git a/tests/disabled_vertices/plugin2/plugin2.go b/tests/disabled_vertices/plugin2/plugin2.go old mode 100644 new mode 100755 diff --git a/tests/disabled_vertices/plugin3/plugin3.go b/tests/disabled_vertices/plugin3/plugin3.go old mode 100644 new mode 100755 diff --git a/tests/disabled_vertices/plugin4/plugin4.go b/tests/disabled_vertices/plugin4/plugin4.go old mode 100644 new mode 100755 diff --git a/tests/disabled_vertices/plugin5/plugin5.go b/tests/disabled_vertices/plugin5/plugin5.go old mode 100644 new mode 100755 diff --git a/tests/disabled_vertices/plugin6/plugin6.go b/tests/disabled_vertices/plugin6/plugin6.go old mode 100644 new mode 100755 diff --git a/tests/disabled_vertices/plugin7/plugin7.go b/tests/disabled_vertices/plugin7/plugin7.go old mode 100644 new mode 100755 diff --git a/tests/disabled_vertices/plugin8/plugin8.go b/tests/disabled_vertices/plugin8/plugin8.go old mode 100644 new mode 100755 diff --git a/tests/disabled_vertices/plugin9/plugin9.go b/tests/disabled_vertices/plugin9/plugin9.go old mode 100644 new mode 100755 diff --git a/tests/happy_scenarios/README.md b/tests/happy_scenarios/README.md old mode 100644 new mode 100755 diff --git a/tests/happy_scenarios/graph.png b/tests/happy_scenarios/graph.png old mode 100644 new mode 100755 diff --git a/tests/happy_scenarios/happyScenario_test.go b/tests/happy_scenarios/happyScenario_test.go old mode 100644 new mode 100755 diff --git a/tests/happy_scenarios/plugin1/plugin1.go b/tests/happy_scenarios/plugin1/plugin1.go old mode 100644 new mode 100755 index e75d3db..b3dcf32 --- a/tests/happy_scenarios/plugin1/plugin1.go +++ b/tests/happy_scenarios/plugin1/plugin1.go @@ -8,7 +8,7 @@ import ( type S1 struct { } -func (s1 *S1) Depends() []interface{} { +func (s1 *S1) Collects() []interface{} { return []interface{}{ s1.AddService, } @@ -18,7 +18,7 @@ func (s1 *S1) AddService(svc *plugin4.DB) error { return nil } -// Depends on S2 and DB (S3 in the current case) +// Collects on S2 and DB (S3 in the current case) func (s1 *S1) Init(s2 *plugin2.S2, db *plugin2.DB) error { return nil } diff --git a/tests/happy_scenarios/plugin2/plugin2.go b/tests/happy_scenarios/plugin2/plugin2.go old mode 100644 new mode 100755 diff --git a/tests/happy_scenarios/plugin3/plugin3.go b/tests/happy_scenarios/plugin3/plugin3.go old mode 100644 new mode 100755 index 782db72..0175df6 --- a/tests/happy_scenarios/plugin3/plugin3.go +++ b/tests/happy_scenarios/plugin3/plugin3.go @@ -8,7 +8,7 @@ import ( type S3 struct { } -func (s3 *S3) Depends() []interface{} { +func (s3 *S3) Collects() []interface{} { return []interface{}{ s3.SomeOtherDep, } @@ -18,7 +18,7 @@ func (s3 *S3) SomeOtherDep(svc *plugin4.S4, svc2 plugin2.S2) error { return nil } -// Depends on S3 +// Collects on S3 func (s3 *S3) Init(svc plugin2.S2) error { return nil } diff --git a/tests/happy_scenarios/plugin4/plugin4.go b/tests/happy_scenarios/plugin4/plugin4.go old mode 100644 new mode 100755 index 3643131..f8a16e0 --- a/tests/happy_scenarios/plugin4/plugin4.go +++ b/tests/happy_scenarios/plugin4/plugin4.go @@ -33,7 +33,7 @@ func (s *S4) CreateAnotherDB() (*DB, error) { }, nil } -func (s *S4) Depends() []interface{} { +func (s *S4) Collects() []interface{} { return []interface{}{ s.AddService, } diff --git a/tests/happy_scenarios/plugin5/plugin5.go b/tests/happy_scenarios/plugin5/plugin5.go old mode 100644 new mode 100755 diff --git a/tests/happy_scenarios/plugin6/plugin6.go b/tests/happy_scenarios/plugin6/plugin6.go old mode 100644 new mode 100755 diff --git a/tests/happy_scenarios/plugin7/plugin7.go b/tests/happy_scenarios/plugin7/plugin7.go old mode 100644 new mode 100755 diff --git a/tests/happy_scenarios/plugin8/plugin8.go b/tests/happy_scenarios/plugin8/plugin8.go old mode 100644 new mode 100755 index 3403853..a3235b5 --- a/tests/happy_scenarios/plugin8/plugin8.go +++ b/tests/happy_scenarios/plugin8/plugin8.go @@ -3,7 +3,7 @@ package primitive type Plugin8 struct { } -// Depends on S2 and DB (S3 in the current case) +// Collects on S2 and DB (S3 in the current case) func (f *Plugin8) Init(a int) error { return nil } diff --git a/tests/happy_scenarios/provided_value_but_need_pointer/plugin1/foo2_value.go b/tests/happy_scenarios/provided_value_but_need_pointer/plugin1/foo2_value.go old mode 100644 new mode 100755 diff --git a/tests/happy_scenarios/provided_value_but_need_pointer/plugin2/foo4_value.go b/tests/happy_scenarios/provided_value_but_need_pointer/plugin2/foo4_value.go old mode 100644 new mode 100755 diff --git a/tests/interfaces/graph.png b/tests/interfaces/graph.png new file mode 100755 index 0000000000000000000000000000000000000000..c8aaef2bae65d95c06086d044ae7abee6f553737 GIT binary patch literal 95 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhYMw5RAr*6ye*XXezn<;U0WUQr t(WZkdl74BVF&^canAOk7#K7?XKkp$n{pY8?wgGi8c)I$ztaD0e0sy549m)Uz literal 0 HcmV?d00001 diff --git a/tests/interfaces/interfaces_test.go b/tests/interfaces/interfaces_test.go old mode 100644 new mode 100755 index ae0cb2e..0fa76ee --- a/tests/interfaces/interfaces_test.go +++ b/tests/interfaces/interfaces_test.go @@ -47,7 +47,7 @@ func TestEndure_Interfaces_OK(t *testing.T) { time.Sleep(time.Second * 1) } -func TestEndure_InterfacesDepends_Ok(t *testing.T) { +func TestEndure_InterfacesCollects_Ok(t *testing.T) { c, err := endure.NewContainer(endure.DebugLevel) assert.NoError(t, err) diff --git a/tests/interfaces/named/randominterface/plugin1.go b/tests/interfaces/named/randominterface/plugin1.go old mode 100644 new mode 100755 diff --git a/tests/interfaces/named/randominterface/plugin2.go b/tests/interfaces/named/randominterface/plugin2.go old mode 100644 new mode 100755 diff --git a/tests/interfaces/named/registers/plugin1.go b/tests/interfaces/named/registers/plugin1.go old mode 100644 new mode 100755 diff --git a/tests/interfaces/named/registers/plugin2.go b/tests/interfaces/named/registers/plugin2.go old mode 100644 new mode 100755 diff --git a/tests/interfaces/named/registersfail/plugin1.go b/tests/interfaces/named/registersfail/plugin1.go old mode 100644 new mode 100755 diff --git a/tests/interfaces/named/registersfail/plugin2.go b/tests/interfaces/named/registersfail/plugin2.go old mode 100644 new mode 100755 diff --git a/tests/interfaces/plugins/plugin1/plugin1.go b/tests/interfaces/plugins/plugin1/plugin1.go old mode 100644 new mode 100755 diff --git a/tests/interfaces/plugins/plugin2/plugin2.go b/tests/interfaces/plugins/plugin2/plugin2.go old mode 100644 new mode 100755 diff --git a/tests/interfaces/plugins/plugin3/plugin3.go b/tests/interfaces/plugins/plugin3/plugin3.go old mode 100644 new mode 100755 diff --git a/tests/interfaces/plugins/plugin4/plugin4.go b/tests/interfaces/plugins/plugin4/plugin4.go old mode 100644 new mode 100755 diff --git a/tests/interfaces/plugins/plugin5/plugin5.go b/tests/interfaces/plugins/plugin5/plugin5.go old mode 100644 new mode 100755 index b8b2f82..d5d579d --- a/tests/interfaces/plugins/plugin5/plugin5.go +++ b/tests/interfaces/plugins/plugin5/plugin5.go @@ -30,7 +30,7 @@ func (f9 *Plugin5) Stop() error { return nil } -func (f9 *Plugin5) Depends() []interface{} { +func (f9 *Plugin5) Collects() []interface{} { return []interface{}{ f9.AddMiddleware, } diff --git a/tests/interfaces/service/not_implemented_service/plugin1/plugin1.go b/tests/interfaces/service/not_implemented_service/plugin1/plugin1.go old mode 100644 new mode 100755 diff --git a/tests/interfaces/service/not_implemented_service/plugin2/plugin2.go b/tests/interfaces/service/not_implemented_service/plugin2/plugin2.go old mode 100644 new mode 100755 diff --git a/tests/issues/issue33/issue33.go b/tests/issues/issue33/issue33.go old mode 100644 new mode 100755 index facf9dc..87d390c --- a/tests/issues/issue33/issue33.go +++ b/tests/issues/issue33/issue33.go @@ -26,7 +26,7 @@ func (f *Plugin1) Provides() []interface{} { } func (f *Plugin1) AddService(dep2 Plugin2) error { - return errors.New("test dependers error") + return errors.New("test collectors error") } type Plugin2 struct { diff --git a/tests/issues/issues_test.go b/tests/issues/issues_test.go old mode 100644 new mode 100755 diff --git a/tests/stress/DependerFuncReturn/foo.go b/tests/stress/CollectorFuncReturn/foo.go old mode 100644 new mode 100755 similarity index 81% rename from tests/stress/DependerFuncReturn/foo.go rename to tests/stress/CollectorFuncReturn/foo.go index 24b96c5..b1c87db --- a/tests/stress/DependerFuncReturn/foo.go +++ b/tests/stress/CollectorFuncReturn/foo.go @@ -1,4 +1,4 @@ -package DependerFuncReturn +package CollectorFuncReturn import "errors" @@ -19,14 +19,14 @@ func (f *FooDep) Stop() error { return nil } -func (f *FooDep) Depends() []interface{} { +func (f *FooDep) Collects() []interface{} { return []interface{}{ f.AddService, } } func (f *FooDep) AddService(dep2 FooDep2) error { - return errors.New("test dependers error") + return errors.New("test collectors error") } type FooDep2 struct { diff --git a/tests/stress/InitErr/foo1_init_err.go b/tests/stress/InitErr/foo1_init_err.go old mode 100644 new mode 100755 diff --git a/tests/stress/InitErr/foo2_init_err.go b/tests/stress/InitErr/foo2_init_err.go old mode 100644 new mode 100755 diff --git a/tests/stress/README.md b/tests/stress/README.md old mode 100644 new mode 100755 diff --git a/tests/stress/ServeErr/foo1_serve_error.go b/tests/stress/ServeErr/foo1_serve_error.go old mode 100644 new mode 100755 diff --git a/tests/stress/ServeErr/foo3_serve_error.go b/tests/stress/ServeErr/foo3_serve_error.go old mode 100644 new mode 100755 index 126bf02..a1180bb --- a/tests/stress/ServeErr/foo3_serve_error.go +++ b/tests/stress/ServeErr/foo3_serve_error.go @@ -3,7 +3,7 @@ package ServeErr type S3ServeError struct { } -func (s3 *S3ServeError) Depends() []interface{} { +func (s3 *S3ServeError) Collects() []interface{} { return []interface{}{ s3.SomeOtherDep, } @@ -13,7 +13,7 @@ func (s3 *S3ServeError) SomeOtherDep(svc *S4ServeError, svc2 S2) error { return nil } -// Depends on S3 +// Collects on S3 func (s3 *S3ServeError) Init(svc S2) error { return nil } diff --git a/tests/stress/ServeErr/foo4ServeError.go b/tests/stress/ServeErr/foo4ServeError.go old mode 100644 new mode 100755 index 51f8d70..5a89d76 --- a/tests/stress/ServeErr/foo4ServeError.go +++ b/tests/stress/ServeErr/foo4ServeError.go @@ -30,9 +30,7 @@ func (s *S4ServeError) CreateAnotherDB() (*FOO4DB, error) { func (s *S4ServeError) Serve() chan error { errCh := make(chan error, 1) - go func() { - errCh <- errors.E(errors.Op("S4Serve"), errors.Serve, errors.Str("s4 test error")) - }() + errCh <- errors.E(errors.Op("S4Serve"), errors.Serve, errors.Str("s4 test error")) return errCh } diff --git a/tests/stress/ServeErr/plugin2.go b/tests/stress/ServeErr/plugin2.go old mode 100644 new mode 100755 diff --git a/tests/stress/ServeErr/plugin5.go b/tests/stress/ServeErr/plugin5.go old mode 100644 new mode 100755 diff --git a/tests/stress/ServeRetryErr/plugin1.go b/tests/stress/ServeRetryErr/plugin1.go old mode 100644 new mode 100755 diff --git a/tests/stress/ServeRetryErr/plugin2.go b/tests/stress/ServeRetryErr/plugin2.go old mode 100644 new mode 100755 diff --git a/tests/stress/ServeRetryErr/plugin3.go b/tests/stress/ServeRetryErr/plugin3.go old mode 100644 new mode 100755 index 4954754..85a665a --- a/tests/stress/ServeRetryErr/plugin3.go +++ b/tests/stress/ServeRetryErr/plugin3.go @@ -9,7 +9,7 @@ import ( type S3 struct { } -func (s3 *S3) Depends() []interface{} { +func (s3 *S3) Collects() []interface{} { return []interface{}{ s3.SomeOtherDep, } @@ -19,7 +19,7 @@ func (s3 *S3) SomeOtherDep(svc *S4, svc2 S2) error { return nil } -// Depends on S3 +// Collects on S3 func (s3 *S3) Init(svc S2) error { return nil } @@ -36,7 +36,7 @@ func (s3 *S3) Stop() error { type S3Init struct { } -func (s3 *S3Init) Depends() []interface{} { +func (s3 *S3Init) Collects() []interface{} { return []interface{}{ s3.SomeOtherDep, } @@ -46,7 +46,7 @@ func (s3 *S3Init) SomeOtherDep(svc *S4, svc2 S2) error { return nil } -// Depends on S3 +// Collects on S3 func (s3 *S3Init) Init(svc S2) error { const Op = "S3Init_Init" s := rand.Intn(10) diff --git a/tests/stress/ServeRetryErr/plugin4.go b/tests/stress/ServeRetryErr/plugin4.go old mode 100644 new mode 100755 diff --git a/tests/stress/ServeRetryErr/plugin5.go b/tests/stress/ServeRetryErr/plugin5.go old mode 100644 new mode 100755 diff --git a/tests/stress/ServeRetryErr/plugin6.go b/tests/stress/ServeRetryErr/plugin6.go old mode 100644 new mode 100755 diff --git a/tests/stress/stress_test.go b/tests/stress/stress_test.go old mode 100644 new mode 100755 index 39c8ffd..64f6b29 --- a/tests/stress/stress_test.go +++ b/tests/stress/stress_test.go @@ -6,7 +6,7 @@ import ( "github.com/spiral/endure" "github.com/spiral/endure/errors" - "github.com/spiral/endure/tests/stress/DependerFuncReturn" + "github.com/spiral/endure/tests/stress/CollectorFuncReturn" "github.com/spiral/endure/tests/stress/InitErr" "github.com/spiral/endure/tests/stress/ServeErr" "github.com/spiral/endure/tests/stress/ServeRetryErr" @@ -36,24 +36,8 @@ func TestEndure_Serve_Err(t *testing.T) { t.Fatal(err) } - res, err := c.Serve() - if err != nil { - t.Fatal(err) - } - - wg := &sync.WaitGroup{} - wg.Add(1) - go func() { - for r := range res { // <--- Error is HERE - assert.Equal(t, "ServeErr.S4ServeError", r.VertexID) - assert.Error(t, r.Error) - assert.NoError(t, c.Stop()) - wg.Done() - return - } - }() - - wg.Wait() + _, err = c.Serve() + assert.Error(t, err) } /* The scenario for this test is the following: @@ -220,12 +204,12 @@ func TestEndure_NoRegisterInvoke(t *testing.T) { assert.NoError(t, c.Stop()) } -func TestEndure_DependerFuncReturnError(t *testing.T) { +func TestEndure_CollectorFuncReturnError(t *testing.T) { c, err := endure.NewContainer(endure.DebugLevel, endure.RetryOnFail(true)) assert.NoError(t, err) - assert.NoError(t, c.Register(&DependerFuncReturn.FooDep{})) - assert.NoError(t, c.Register(&DependerFuncReturn.FooDep2{})) + assert.NoError(t, c.Register(&CollectorFuncReturn.FooDep{})) + assert.NoError(t, c.Register(&CollectorFuncReturn.FooDep2{})) assert.Error(t, c.Init()) _, _ = c.Serve() diff --git a/utils.go b/utils.go old mode 100644 new mode 100755 From d30d9957d97f084f07f8ad091f8b583d7d587ddf Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Wed, 28 Oct 2020 15:45:24 +0300 Subject: [PATCH 2/4] Delete launch.json --- .vscode/launch.json | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100755 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100755 index f5bfb2e..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Launch", - "type": "godlvdap", - "request": "launch", - "mode": "auto", - "program": "${fileDirname}", - "env": {}, - "args": [] - } - ] -} \ No newline at end of file From 75d649bf74a8aac918af21fc15454da6b456137d Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Wed, 28 Oct 2020 15:47:31 +0300 Subject: [PATCH 3/4] Update endure.go --- endure.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/endure.go b/endure.go index 682c318..07a117f 100755 --- a/endure.go +++ b/endure.go @@ -176,9 +176,7 @@ func Visualize(print bool) Options { } } -// Collector depends the dependencies -// name is a name of the dependency, for example - S2 -// vertex is a value -> pointer to the structure +// Register registers the dependencies in the Endure graph without invoking any methods func (e *Endure) Register(vertex interface{}) error { const op = errors.Op("Register") t := reflect.TypeOf(vertex) From 27cf040ddefbd00f8efe9c092c757f933ef45710 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Wed, 28 Oct 2020 20:34:55 +0300 Subject: [PATCH 4/4] Add test for the issue55. Move errors package to the spiral/error --- calculate_deps.go | 2 +- endure.go | 4 +- errors/debug_cap.go | 10 - errors/debug_stack.go | 116 ---------- errors/debug_test.go | 74 ------- errors/errors.go | 241 --------------------- errors/errors_test.go | 155 ------------- errors/marshal.go | 102 --------- go.mod | 1 + go.sum | 2 + internal.go | 10 +- reflect.go | 2 +- structures/visualize_graph.go | 2 +- structures/visualize_graph_windows.go | 2 +- tests/disabled_vertices/plugin1/plugin1.go | 2 +- tests/disabled_vertices/plugin3/plugin3.go | 2 +- tests/disabled_vertices/plugin4/plugin4.go | 2 +- tests/disabled_vertices/plugin6/plugin6.go | 2 +- tests/issues/issue55/graph.png | Bin 0 -> 15630 bytes tests/issues/issue55/plugin1/plugin1.go | 19 ++ tests/issues/issue55/plugin2/plugin2.go | 26 +++ tests/issues/issue55/plugin3/plugin3.go | 17 ++ tests/issues/issues_test.go | 53 +++++ tests/stress/ServeErr/foo4ServeError.go | 2 +- tests/stress/ServeRetryErr/plugin1.go | 2 +- tests/stress/ServeRetryErr/plugin3.go | 2 +- tests/stress/stress_test.go | 2 +- 27 files changed, 137 insertions(+), 717 deletions(-) delete mode 100755 errors/debug_cap.go delete mode 100755 errors/debug_stack.go delete mode 100755 errors/debug_test.go delete mode 100755 errors/errors.go delete mode 100755 errors/errors_test.go delete mode 100755 errors/marshal.go create mode 100644 tests/issues/issue55/graph.png create mode 100644 tests/issues/issue55/plugin1/plugin1.go create mode 100644 tests/issues/issue55/plugin2/plugin2.go create mode 100644 tests/issues/issue55/plugin3/plugin3.go diff --git a/calculate_deps.go b/calculate_deps.go index 1b259e8..dbeea23 100755 --- a/calculate_deps.go +++ b/calculate_deps.go @@ -4,8 +4,8 @@ import ( "fmt" "reflect" - "github.com/spiral/endure/errors" "github.com/spiral/endure/structures" + "github.com/spiral/errors" "go.uber.org/zap" ) diff --git a/endure.go b/endure.go index 07a117f..873a4f5 100755 --- a/endure.go +++ b/endure.go @@ -10,8 +10,8 @@ import ( "sync" "time" - "github.com/spiral/endure/errors" "github.com/spiral/endure/structures" + "github.com/spiral/errors" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) @@ -264,7 +264,7 @@ func (e *Endure) Init() error { } // Serve starts serving the graph -// This is the initial serve, if error produced immedeately in the initial serve, endure will traverse deps back, call stop and exit +// This is the initial serve, if error produced immediately in the initial serve, endure will traverse deps back, call stop and exit func (e *Endure) Serve() (<-chan *Result, error) { e.mutex.Lock() defer e.mutex.Unlock() diff --git a/errors/debug_cap.go b/errors/debug_cap.go deleted file mode 100755 index 2c8a2f7..0000000 --- a/errors/debug_cap.go +++ /dev/null @@ -1,10 +0,0 @@ -// +build !debug - -package errors - -import "bytes" - -type stack struct{} - -func (e *Error) populateStack() {} -func (e *Error) printStack(*bytes.Buffer) {} diff --git a/errors/debug_stack.go b/errors/debug_stack.go deleted file mode 100755 index fa77ddc..0000000 --- a/errors/debug_stack.go +++ /dev/null @@ -1,116 +0,0 @@ -// +build debug - -package errors - -import ( - "bytes" - "fmt" - "runtime" - "strings" -) - -type stack struct { - callers []uintptr - // TODO(adg): add time of creation -} - -func (e *Error) populateStack() { - e.callers = callers() - - e2, ok := e.Err.(*Error) - if !ok { - return - } - - i := 0 - - ok = false - for ; i < len(e.callers) && i < len(e2.callers); i++ { - // check for similar - if e.callers[len(e.callers)-1-i] != e2.callers[len(e2.callers)-1-i] { - break - } - ok = true - } - - if ok { //we have common PCs - e2Head := e2.callers[:len(e2.callers)-i] - eTail := e.callers - - e.callers = make([]uintptr, len(e2Head)+len(eTail)) - - copy(e.callers, e2Head) - copy(e.callers[len(e2Head):], eTail) - - e2.callers = nil - } -} - -// frame returns the nth frame, with the frame at top of stack being 0. -func frame(callers []uintptr, n int) runtime.Frame { - frames := runtime.CallersFrames(callers) - var f runtime.Frame - for i := len(callers) - 1; i >= n; i-- { - var ok bool - f, ok = frames.Next() - if !ok { - break - } - } - return f -} - -func (e *Error) printStack(b *bytes.Buffer) { - c := callers() - - var prev string - var diff bool - for i := 0; i < len(e.callers); i++ { - pc := e.callers[len(e.callers)-i-1] // get current PC - fn := runtime.FuncForPC(pc) // get function by pc - name := fn.Name() - - if !diff && i < len(c) { - ppc := c[len(c)-i-1] - pname := runtime.FuncForPC(ppc).Name() - if name == pname { - continue - } - diff = true - } - - if name == prev { - continue - } - - trim := 0 - for { - j := strings.IndexAny(name[trim:], "./") - if j < 0 { - break - } - if !strings.HasPrefix(prev, name[:j+trim]) { - break - } - trim += j + 1 // skip over the separator - } - - // Do the printing. - appendStrToBuf(b, Separator) - file, line := fn.FileLine(pc) - fmt.Fprintf(b, "%v:%d: ", file, line) - if trim > 0 { - b.WriteString("...") - } - b.WriteString(name[trim:]) - - prev = name - } -} - -func callers() []uintptr { - var stk [64]uintptr - const skip = 4 - n := runtime.Callers(skip, stk[:]) - return stk[:n] -} diff --git a/errors/debug_test.go b/errors/debug_test.go deleted file mode 100755 index 3ce9524..0000000 --- a/errors/debug_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// +build debug - -package errors - -import ( - "fmt" - "regexp" - "strings" - "testing" -) - -var errorLines = strings.Split(strings.TrimSpace(` - .*/errors/debug_test.go:\d+: github.com/spiral/endure/errors.func1: - .*/errors/debug_test.go:\d+: ...T.func2: - .*/errors/debug_test.go:\d+: ...func3: - .*/errors/debug_test.go:\d+: ...func4: func2 invoke func3: Serve error: - func4 operation: error in action -`), "\n") - -var errorLineREs = make([]*regexp.Regexp, len(errorLines)) - -func init() { - for i, s := range errorLines { - errorLineREs[i] = regexp.MustCompile(fmt.Sprintf("^%s", s)) - } -} - -func TestDebug(t *testing.T) { - got := printErr(t, func1()) - lines := strings.Split(got, "\n") - for i, re := range errorLineREs { - if i >= len(lines) { - // Handled by line number check. - break - } - if !re.MatchString(lines[i]) { - t.Errorf("error does not match at line %v, got:\n\t%q\nwant:\n\t%q", i, lines[i], re) - } - } - if got, want := len(lines), len(errorLines); got != want { - t.Errorf("got %v lines of errors, want %v", got, want) - } -} - -type T struct{} - -func printErr(t *testing.T, err error) string { - return err.Error() -} - -func func1() error { - var t T - return t.func2() -} - -func (T) func2() error { - o := Op("func2 invoke func3") - return E(o, func3()) -} - -func func3() error { - return func4() -} - -func func4() error { - o := Op("func4 operation") - return E(o, Serve, Str("error in action")) -} - -///Users/0xdev/Projects/repo/errors/debug_test.go:53: github.com/spiral/endure/errors.func1: -///Users/0xdev/Projects/repo/errors/debug_test.go:58: ...T.func2: -///Users/0xdev/Projects/repo/errors/debug_test.go:62: ...func3: -///Users/0xdev/Projects/repo/errors/debug_test.go:67: ...func4: func2 invoke func3: Serve error: -//func4 operation: error in action diff --git a/errors/errors.go b/errors/errors.go deleted file mode 100755 index e80bef7..0000000 --- a/errors/errors.go +++ /dev/null @@ -1,241 +0,0 @@ -package errors - -import ( - "bytes" - "encoding" - "errors" - "fmt" - "log" - "runtime" -) - -type Error struct { - Op Op - Kind Kind - Err error - - // Stack information - stack -} - -func (e *Error) isZero() bool { - return e.Op == "" && e.Kind == 0 && e.Err == nil -} - -var ( - _ error = (*Error)(nil) - _ encoding.BinaryUnmarshaler = (*Error)(nil) - _ encoding.BinaryMarshaler = (*Error)(nil) -) - -// Op describes an operation -type Op string - -// separator -> new line plus tabulator to intend error if previuos not nil -var Separator = ":\n\t" - -type Kind uint8 - -// Kinds of errors. -const ( - Undefined Kind = iota // Undefined error. - Register - Providers - Logger - ArgType - Init - Serve - Unsupported - Disabled - - Traverse - FunctionCall -) - -func (k Kind) String() string { - switch k { - case Undefined: - return "UNDEF" - case Register: - return "Register error" - case Providers: - return "Providers error" - case Logger: - return "Logger error" - case Init: - return "Init error" - case Serve: - return "Serve error" - case Disabled: - return "Vertex disabled" - case ArgType: - return "Wrong arg type, or return type" - case Traverse: - return "Traverse error" - case FunctionCall: - return "Function call error" - case Unsupported: - return "Unsupported" - default: - return "UNDEF" - } -} - -// E builds an error value from its arguments. -func E(args ...interface{}) error { - e := &Error{} - if len(args) == 0 { - msg := "errors.E called with 0 args" - _, file, line, ok := runtime.Caller(1) - if ok { - msg = fmt.Sprintf("%v - %v:%v", msg, file, line) - } - e.Err = errors.New(msg) - } - - for _, arg := range args { - switch arg := arg.(type) { - case Op: - e.Op = arg - case string: - e.Err = Str(arg) - case Kind: - e.Kind = arg - case *Error: - // Make a copy - eCopy := *arg - e.Err = &eCopy - case error: - e.Err = arg - // add map map[string]string - default: - _, file, line, _ := runtime.Caller(1) - log.Printf("errors.E: bad call from %s:%d: %v", file, line, args) - return Errorf("unknown type %T, value %v in error call", arg, arg) - } - } - - // Populate stack information - e.populateStack() - - prev, ok := e.Err.(*Error) - if !ok { - return e - } - - if prev.Kind == e.Kind { - prev.Kind = Undefined - } - - if e.Kind == Undefined { - e.Kind = prev.Kind - prev.Kind = Undefined - } - return e -} - -func (e *Error) Error() string { - b := new(bytes.Buffer) - e.printStack(b) - if e.Op != "" { - appendStrToBuf(b, ": ") - b.WriteString(string(e.Op)) - } - - if e.Kind != 0 { - appendStrToBuf(b, ": ") - b.WriteString(e.Kind.String()) - } - if e.Err != nil { - if prevErr, ok := e.Err.(*Error); ok { - if !prevErr.isZero() { - // indent - separator - appendStrToBuf(b, Separator) - b.WriteString(e.Err.Error()) - } - } else { - appendStrToBuf(b, ": ") - b.WriteString(e.Err.Error()) - } - } - if b.Len() == 0 { - return "no error" - } - return b.String() -} - -// errors.New -func Str(text string) error { - return &errorString{text} -} - -type errorString struct { - s string -} - -func (e *errorString) Error() string { - return e.s -} - -func Errorf(format string, args ...interface{}) error { - return &errorString{fmt.Sprintf(format, args...)} -} - -func Match(err1, err2 error) bool { - e1, ok := err1.(*Error) - if !ok { - return false - } - e2, ok := err2.(*Error) - if !ok { - return false - } - if e1.Op != "" && e2.Op != e1.Op { - return false - } - if e1.Kind != Undefined && e2.Kind != e1.Kind { - return false - } - if e1.Err != nil { - if _, ok := e1.Err.(*Error); ok { - return Match(e1.Err, e2.Err) - } - if e2.Err == nil || e2.Err.Error() != e1.Err.Error() { - return false - } - } - return true -} - -// Is reports whether err is an *Error of the given Kind -func Is(kind Kind, err error) bool { - e, ok := err.(*Error) - if !ok { - return false - } - if e.Kind != Undefined { - return e.Kind == kind - } - if e.Err != nil { - return Is(kind, e.Err) - } - return false -} - -// Do smt with no care about result (and panics) -func SafelyDo(work func()) { - defer func() { - if err := recover(); err != nil { - log.Printf("work failed: %s", err) - } - }() - - work() -} - -func appendStrToBuf(b *bytes.Buffer, str string) { - if b.Len() == 0 { - return - } - b.WriteString(str) -} diff --git a/errors/errors_test.go b/errors/errors_test.go deleted file mode 100755 index c67ef05..0000000 --- a/errors/errors_test.go +++ /dev/null @@ -1,155 +0,0 @@ -// +build !debug - -package errors - -import ( - "fmt" - "io" - "testing" -) - -func TestMarshal(t *testing.T) { - // Single error. No user is set, so we will have a zero-length field inside. - e1 := E(Op("Get"), Serve, "caching in progress") - - // Nested error. - e2 := E(Op("Read"), Undefined, e1) - - b := MarshalError(e2) - e3 := UnmarshalError(b) - - in := e2.(*Error) - out := e3.(*Error) - - // Compare elementwise. - if in.Op != out.Op { - t.Errorf("expected Op %q; got %q", in.Op, out.Op) - } - if in.Kind != out.Kind { - t.Errorf("expected kind %d; got %d", in.Kind, out.Kind) - } - // Note that error will have lost type information, so just check its Error string. - if in.Err.Error() != out.Err.Error() { - t.Errorf("expected Err %q; got %q", in.Err, out.Err) - } -} - -func TestSeparator(t *testing.T) { - defer func(prev string) { - Separator = prev - }(Separator) - Separator = ":: " - - // Single error. No user is set, so we will have a zero-length field inside. - e1 := E(Op("Get"), Serve, "Serve error") - - // Nested error. - e2 := E(Op("Get"), Serve, e1) - - want := "Get: Serve error:: Get: Serve error" - if errorAsString(e2) != want { - t.Errorf("expected %q; got %q", want, e2) - } -} - -func TestDoesNotChangePreviousError(t *testing.T) { - err := E(Serve) - err2 := E(Op("I will NOT modify err"), err) - - expected := "I will NOT modify err: Serve error" - if errorAsString(err2) != expected { - t.Fatalf("Expected %q, got %q", expected, err2) - } - kind := err.(*Error).Kind - if kind != Serve { - t.Fatalf("Expected kind %v, got %v", Serve, kind) - } -} - -type matchTest struct { - err1, err2 error - matched bool -} - -const ( - op = Op("Op") - op1 = Op("Op1") - op2 = Op("Op2") -) - -var matchTests = []matchTest{ - // Errors not of type *Error fail outright. - {nil, nil, false}, - {io.EOF, io.EOF, false}, - {E(io.EOF), io.EOF, false}, - {io.EOF, E(io.EOF), false}, - // Success. We can drop fields from the first argument and still match. - {E(io.EOF), E(io.EOF), true}, - {E(op, Init, io.EOF), E(op, Init, io.EOF), true}, - {E(op, Init, io.EOF, "test"), E(op, Init, io.EOF, "test", "test"), true}, - {E(op, Init), E(op, Init, io.EOF, "test", "test"), true}, - {E(op), E(op, Init, io.EOF, "test", "test"), true}, - // Failure. - {E(io.EOF), E(io.ErrClosedPipe), false}, - {E(op1), E(op2), false}, - {E(Init), E(Serve), false}, - {E("test"), E("test1"), false}, - {E(fmt.Errorf("error")), E(fmt.Errorf("error1")), false}, - {E(op, Init, io.EOF, "test", "test1"), E(op, Init, io.EOF, "test", "test"), false}, - {E("test", Str("something")), E("test"), false}, // Test nil error on rhs. - // Nested *Errors. - {E(op1, E("test")), E(op1, "1", E(op2, "2", "test")), true}, - {E(op1, "test"), E(op1, "1", E(op2, "2", "test")), false}, - {E(op1, E("test")), E(op1, "1", Str(E(op2, "2", "test").Error())), false}, -} - -func TestMatch(t *testing.T) { - for _, test := range matchTests { - matched := Match(test.err1, test.err2) - if matched != test.matched { - t.Errorf("Match(%q, %q)=%t; want %t", test.err1, test.err2, matched, test.matched) - } - } -} - -type kindTest struct { - err error - kind Kind - want bool -} - -var kindTests = []kindTest{ - // Non-Error errors. - {nil, Serve, false}, - {Str("not an *Error"), Serve, false}, - - // Basic comparisons. - {E(Serve), Serve, true}, - {E(Init), Serve, false}, - {E("no kind"), Serve, false}, - {E("no kind"), Logger, false}, - - // Nested *Error values. - {E("Nesting", E(Serve)), Serve, true}, - {E("Nesting", E(Logger)), Serve, false}, - {E("Nesting", E("no kind")), Serve, false}, - {E("Nesting", E("no kind")), Logger, false}, -} - -func TestKind(t *testing.T) { - for _, test := range kindTests { - got := Is(test.kind, test.err) - if got != test.want { - t.Errorf("Is(%q, %q)=%t; want %t", test.kind, test.err, got, test.want) - } - } -} - -func errorAsString(err error) string { - if e, ok := err.(*Error); ok { - e2 := *e - e2.stack = stack{} - return e2.Error() - } - return err.Error() -} diff --git a/errors/marshal.go b/errors/marshal.go deleted file mode 100755 index 7c8a63e..0000000 --- a/errors/marshal.go +++ /dev/null @@ -1,102 +0,0 @@ -package errors - -import ( - "encoding/binary" - "log" -) - -func (e *Error) MarshalAppend(b []byte) []byte { - if e == nil { - return b - } - - b = appendString(b, string(e.Op)) - - var tmp [16]byte - N := binary.PutVarint(tmp[:], int64(e.Kind)) - b = append(b, tmp[:N]...) - b = MarshalErrorAppend(e.Err, b) - return b -} - -func (e *Error) MarshalBinary() ([]byte, error) { - return e.MarshalAppend(nil), nil -} - -func MarshalErrorAppend(err error, b []byte) []byte { - if err == nil { - return b - } - if e, ok := err.(*Error); ok { - b = append(b, 'E') - return e.MarshalAppend(b) - } - // Ordinary error. - b = append(b, 'e') - b = appendString(b, err.Error()) - return b -} - -func MarshalError(err error) []byte { - return MarshalErrorAppend(err, nil) -} - -func (e *Error) UnmarshalBinary(b []byte) error { - if len(b) == 0 { - return nil - } - data, b := getBytes(b) - if data != nil { - e.Op = Op(data) - } - k, N := binary.Varint(b) - e.Kind = Kind(k) - b = b[N:] - e.Err = UnmarshalError(b) - return nil -} - -func UnmarshalError(b []byte) error { - if len(b) == 0 { - return nil - } - code := b[0] - b = b[1:] - switch code { - case 'e': - var data []byte - data, b = getBytes(b) - if len(b) != 0 { - log.Printf("Unmarshal error: trailing bytes") - } - return Str(string(data)) - case 'E': - var err Error - err.UnmarshalBinary(b) - return &err - default: - log.Printf("Unmarshal error: corrupt data %q", b) - return Str(string(b)) - } -} - -func appendString(b []byte, str string) []byte { - var tmp [16]byte - N := binary.PutUvarint(tmp[:], uint64(len(str))) - b = append(b, tmp[:N]...) - b = append(b, str...) - return b -} - -func getBytes(b []byte) (data, remaining []byte) { - u, N := binary.Uvarint(b) - if len(b) < N+int(u) { - log.Printf("Unmarshal error: bad encoding") - return nil, nil - } - if N == 0 { - log.Printf("Unmarshal error: bad encoding") - return nil, b - } - return b[N : N+int(u)], b[N+int(u):] -} diff --git a/go.mod b/go.mod index 3c15de9..7aca5c8 100755 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.15 require ( github.com/cenkalti/backoff/v4 v4.1.0 github.com/goccy/go-graphviz v0.0.8 + github.com/spiral/errors v0.0.1 github.com/stretchr/testify v1.6.1 go.uber.org/zap v1.16.0 golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect diff --git a/go.sum b/go.sum index e49e868..5bd6d23 100755 --- a/go.sum +++ b/go.sum @@ -29,6 +29,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/spiral/errors v0.0.1 h1:KVLYwAZyXdVBjy8acLue8YK894jsDDriZVuYqypW4gg= +github.com/spiral/errors v0.0.1/go.mod h1:SwMSZVdZkkJVgXNNafccqOaxWg0XPzVU/dEdUEInE0o= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= diff --git a/internal.go b/internal.go index 1f4ed2f..e810239 100755 --- a/internal.go +++ b/internal.go @@ -7,8 +7,8 @@ import ( "time" "github.com/cenkalti/backoff/v4" - "github.com/spiral/endure/errors" "github.com/spiral/endure/structures" + "github.com/spiral/errors" "go.uber.org/zap" ) @@ -424,15 +424,16 @@ Algorithm is the following (all steps executing in the topological order): func (e *Endure) callServeFn(vertex *structures.Vertex, in []reflect.Value) (*result, error) { const op = errors.Op("call_serve_fn") + e.logger.Debug("preparing to serve the vertex", zap.String("vertex id", vertex.ID)) m, _ := reflect.TypeOf(vertex.Iface).MethodByName(ServeMethodName) ret := m.Func.Call(in) res := ret[0].Interface() if res != nil { - e.logger.Debug("start serving vertex", zap.String("vertex id", vertex.ID)) + e.logger.Debug("called serve on the vertex", zap.String("vertex id", vertex.ID)) if e, ok := res.(chan error); ok && e != nil { // error come righth after we start serving the vertex if len(e) > 0 { - return nil, errors.E(op, errors.FunctionCall, errors.Str("got immediate error from vertex, stopping serve execution")) + return nil, errors.E(op, errors.FunctionCall, errors.Str("got first run error from vertex, stopping serve execution")) } return &result{ errCh: e, @@ -588,7 +589,7 @@ func (e *Endure) serve(n *structures.DllNode) error { } // traverseBackStop used to visit every Prev node and stop vertices -func (e *Endure) traverseBackStop(n *structures.DllNode) error { +func (e *Endure) traverseBackStop(n *structures.DllNode) { const op = errors.Op("traverse_back_stop") e.logger.Debug("stopping vertex in the first Serve call", zap.String("vertex id", n.Vertex.ID)) nCopy := n @@ -600,7 +601,6 @@ func (e *Endure) traverseBackStop(n *structures.DllNode) error { } nCopy = nCopy.Prev } - return nil } func (e *Endure) startMainThread() { diff --git a/reflect.go b/reflect.go index 1a69578..299f413 100755 --- a/reflect.go +++ b/reflect.go @@ -6,7 +6,7 @@ import ( "runtime" "strings" - "github.com/spiral/endure/errors" + "github.com/spiral/errors" ) func providersReturnType(m interface{}) (reflect.Type, error) { diff --git a/structures/visualize_graph.go b/structures/visualize_graph.go index a18c053..4d5eaff 100755 --- a/structures/visualize_graph.go +++ b/structures/visualize_graph.go @@ -4,7 +4,7 @@ package structures import ( "github.com/goccy/go-graphviz" - "github.com/spiral/endure/errors" + "github.com/spiral/errors" ) func Visualize(vertices []*Vertex) error { diff --git a/structures/visualize_graph_windows.go b/structures/visualize_graph_windows.go index afa0ffd..5db3a15 100755 --- a/structures/visualize_graph_windows.go +++ b/structures/visualize_graph_windows.go @@ -3,7 +3,7 @@ package structures import ( - "github.com/spiral/endure/errors" + "github.com/spiral/errors" ) func Visualize(vertices []*Vertex) error { diff --git a/tests/disabled_vertices/plugin1/plugin1.go b/tests/disabled_vertices/plugin1/plugin1.go index fdd6e8d..702c539 100755 --- a/tests/disabled_vertices/plugin1/plugin1.go +++ b/tests/disabled_vertices/plugin1/plugin1.go @@ -1,6 +1,6 @@ package plugin1 -import "github.com/spiral/endure/errors" +import "github.com/spiral/errors" type Plugin1 struct { } diff --git a/tests/disabled_vertices/plugin3/plugin3.go b/tests/disabled_vertices/plugin3/plugin3.go index 0cfbdcb..6f63f1c 100755 --- a/tests/disabled_vertices/plugin3/plugin3.go +++ b/tests/disabled_vertices/plugin3/plugin3.go @@ -1,7 +1,7 @@ package plugin3 import ( - "github.com/spiral/endure/errors" + "github.com/spiral/errors" ) type Super interface { diff --git a/tests/disabled_vertices/plugin4/plugin4.go b/tests/disabled_vertices/plugin4/plugin4.go index c4b4c22..57b2af4 100755 --- a/tests/disabled_vertices/plugin4/plugin4.go +++ b/tests/disabled_vertices/plugin4/plugin4.go @@ -1,6 +1,6 @@ package plugin4 -import "github.com/spiral/endure/errors" +import "github.com/spiral/errors" type Plugin4 struct { } diff --git a/tests/disabled_vertices/plugin6/plugin6.go b/tests/disabled_vertices/plugin6/plugin6.go index d690a5e..6dfc7b9 100755 --- a/tests/disabled_vertices/plugin6/plugin6.go +++ b/tests/disabled_vertices/plugin6/plugin6.go @@ -1,7 +1,7 @@ package plugin6 import ( - "github.com/spiral/endure/errors" + "github.com/spiral/errors" ) type Plugin6 struct { diff --git a/tests/issues/issue55/graph.png b/tests/issues/issue55/graph.png new file mode 100644 index 0000000000000000000000000000000000000000..402dcae0401f2c47644d10ed1bfc427e85df3bf5 GIT binary patch literal 15630 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