diff --git a/.gitignore b/.gitignore
index acd641a1..8cc37593 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,5 +8,5 @@ tags
.session
cover.out
tests/cover.out
-examples/sandbox
+sandbox
diff --git a/ChangeLog.md b/ChangeLog.md
index 0bd52aa7..dda309c9 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -4,11 +4,25 @@ All notable changes to this project will be documented in this file.
This format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+#### [v2.2.1](https://github.com/ergo-services/ergo/releases/tag/v1.999.221) 2023-01-18 [tag version v1.999.221] ####
+
+* Now you can join your services made with Ergo Framework into a single cluster with transparent networking using our **Cloud Overlay Network** where they can connect to each other smoothly, no matter where they run - AWS, Azure or GCP, or anywhere else. All these connections are secured with end-to-end encryption. Read more in this article [https://https://medium.com/@ergo-services/cloud-overlay-network](https://https://medium.com/@ergo-services/cloud-overlay-network). Here is an example of this feature in action [examples/cloud](https://github.com/ergo-services/examples/tree/master/cloud)
+* `examples` moved to https://github.com/ergo-services/examples
+* Added support Erlang OTP/25
+* Improved handling `nil` values for the registered types using `etf.RegisterType(...)`
+* Improved self-signed certificate generation
+* Introduced `ergo.debug` option that enables extended debug information for `lib.Log(...)`/`lib.Warning(...)`
+* Fixed `gen.TCP` and `gen.UDP` (missing callbacks)
+* Fixed ETF registering type with `etf.Pid`, `etf.Alias` or `etf.Ref` value types
+* Fixed Cloud client
+* Fixed #117 (incorrect hanshake process finalization)
+* Fixed #139 (panic of the gen.Stage partition dispatcher)
+
#### [v2.2.0](https://github.com/ergo-services/ergo/releases/tag/v1.999.220) 2022-10-18 [tag version v1.999.220] ####
-* Introduced `gen.Web` behavior. It implements **Web API Gateway pattern** is also sometimes known as the "Backend For Frontend" (BFF). See example [examples/genweb](examples/genweb)
-* Introduced `gen.TCP` behavior - **socket acceptor pool for TCP protocols**. It provides everything you need to accept TCP connections and process packets with a small code base and low latency. Here is simple example [examples/gentcp](examples/gentcp)
-* Introduced `gen.UDP` - the same as `gen.TCP`, but for UDP protocols. Example is here [examples/genudp](examples/genudp)
+* Introduced `gen.Web` behavior. It implements **Web API Gateway pattern** is also sometimes known as the "Backend For Frontend" (BFF). See example [examples/genweb](https://github.com/ergo-services/examples/tree/master/genweb)
+* Introduced `gen.TCP` behavior - **socket acceptor pool for TCP protocols**. It provides everything you need to accept TCP connections and process packets with a small code base and low latency. Here is simple example [examples/gentcp](https://github.com/ergo-services/examples/tree/master/gentcp)
+* Introduced `gen.UDP` - the same as `gen.TCP`, but for UDP protocols. Example is here [examples/genudp](https://github.com/ergo-services/examples/tree/master/genudp)
* Introduced **Events**. This is a simple pub/sub feature within a node - any `gen.Process` can become a producer by registering a new event `gen.Event` using method `gen.Process.RegisterEvent`, while the others can subscribe to these events using `gen.Process.MonitorEvent`. Subscriber process will also receive `gen.MessageEventDown` if a producer process went down (terminated). This feature behaves in a monitor manner but only works within a node. You may also want to subscribe to a system event - `node.EventNetwork` to receive event notification on connect/disconnect any peers.
* Introduced **Cloud Client** - allows connecting to the cloud platform [https://ergo.sevices](https://ergo.services). You may want to register your email there, and we will inform you about the platform launch day
* Introduced **type registration** for the ETF encoding/decoding. This feature allows you to get rid of manually decoding with `etf.TermIntoStruct` for the receiving messages. Register your type using `etf.RegisterType(...)`, and you will be receiving messages in a native type
@@ -41,9 +55,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `node.Options`:
- `Proxy` for configuring proxy settings
- includes support (over the proxy connection): compression, fragmentation, link/monitor process, monitor node
- - example [examples/proxy](examples/proxy).
+ - example [examples/proxy](https://github.com/ergo-services/examples/tree/master/proxy).
- this feature is not available for the Erlang/Elixir nodes
-* Introduced **behavior `gen.Raft`**. It's improved implementation of [Raft consensus algorithm](https://raft.github.io). The key improvement is using quorum under the hood to manage the leader election process and make the Raft cluster more reliable. This implementation supports quorums of 3, 5, 7, 9, or 11 quorum members. Here is an example of this feature [examples/genraft](examples/genraft).
+* Introduced **behavior `gen.Raft`**. It's improved implementation of [Raft consensus algorithm](https://raft.github.io). The key improvement is using quorum under the hood to manage the leader election process and make the Raft cluster more reliable. This implementation supports quorums of 3, 5, 7, 9, or 11 quorum members. Here is an example of this feature [examples/genraft](https://github.com/ergo-services/examples/tree/master/genraft).
* Introduced **interfaces to customize network layer**
- `Resolver` to replace EPMD routines with your solution (e.g., ZooKeeper or any other service registrar)
- `Handshake` allows customizing authorization/authentication process
@@ -72,7 +86,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Added support of Erlang/OTP 24 (including [Alias](https://blog.erlang.org/My-OTP-24-Highlights/#eep-53-process-aliases) feature and [Remote Spawn](https://blog.erlang.org/OTP-23-Highlights/#distributed-spawn-and-the-new-erpc-module) introduced in Erlang/OTP 23)
* **Important**: This release includes refined API (without backward compatibility) for a more convenient way to create OTP-designed microservices. Make sure to update your code.
* **Important**: Project repository has been moved to [https://github.com/ergo-services/ergo](https://github.com/ergo-services/ergo). It is still available on the old URL [https://github.com/halturin/ergo](https://github.com/halturin/ergo) and GitHub will redirect all requests to the new one (thanks to GitHub for this feature).
-* Introduced new behavior `gen.Saga`. It implements Saga design pattern - a sequence of transactions that updates each service state and publishes the result (or cancels the transaction or triggers the next transaction step). `gen.Saga` also provides a feature of interim results (can be used as transaction progress or as a part of pipeline processing), time deadline (to limit transaction lifespan), two-phase commit (to make distributed transaction atomic). Here is example [examples/gensaga](examples/gensaga).
+* Introduced new behavior `gen.Saga`. It implements Saga design pattern - a sequence of transactions that updates each service state and publishes the result (or cancels the transaction or triggers the next transaction step). `gen.Saga` also provides a feature of interim results (can be used as transaction progress or as a part of pipeline processing), time deadline (to limit transaction lifespan), two-phase commit (to make distributed transaction atomic). Here is example [examples/gensaga](https://github.com/ergo-services/examples/tree/master/gensaga).
* Introduced new methods `Process.Direct` and `Process.DirectWithTimeout` to make direct request to the actor (`gen.Server` or inherited object). If an actor has no implementation of `HandleDirect` callback it returns `ErrUnsupportedRequest` as a error.
* Introduced new callback `HandleDirect` in the `gen.Server` interface as a handler for requests made by `Process.Direct` or `Process.DirectWithTimeout`. It should be easy to interact with actors from outside.
* Introduced new types intended to be used to interact with Erlang/Elixir
@@ -82,8 +96,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Introduced new methods `Node.ProvideRemoteSpawn`, `Node.RevokeRemoteSpawn`, `Process.RemoteSpawn`.
* Introduced new interfaces `Marshaler` (method `MarshalETF`) and `Unmarshaler` (method `UnmarshalETF`) for the custom encoding/decoding data.
* Improved performance for the local messaging (up to 3 times for some cases)
-* Added example [examples/http](examples/http) to demonsrate how HTTP server can be integrated into the Ergo node.
-* Added example [examples/gendemo](examples/gendemo) - how to create a custom behavior (design pattern) on top of the `gen.Server`. Take inspiration from the [gen/stage.go](gen/stage.go) or [gen/saga.go](gen/saga.go) design patterns.
+* Added example [examples/http](https://github.com/ergo-services/examples/tree/master/http) to demonsrate how HTTP server can be integrated into the Ergo node.
+* Added example [examples/gendemo](https://github.com/ergo-services/examples/tree/master/gendemo) - how to create a custom behavior (design pattern) on top of the `gen.Server`. Take inspiration from the [gen/stage.go](gen/stage.go) or [gen/saga.go](gen/saga.go) design patterns.
* Added support FreeBSD, OpenBSD, NetBSD, DragonFly.
* Fixed RPC issue #45
* Fixed internal timer issue #48
diff --git a/README.md b/README.md
index 6f4b9fe5..7cceaa89 100644
--- a/README.md
+++ b/README.md
@@ -17,11 +17,15 @@ The easiest way to create an OTP-designed application in Golang.
The goal of this project is to leverage Erlang/OTP experience with Golang performance. Ergo Framework implements [DIST protocol](https://erlang.org/doc/apps/erts/erl_dist_protocol.html), [ETF data format](https://erlang.org/doc/apps/erts/erl_ext_dist.html) and [OTP design patterns](https://erlang.org/doc/design_principles/des_princ.html) `gen.Server`, `gen.Supervisor`, `gen.Application` which makes you able to create distributed, high performance and reliable microservice solutions having native integration with Erlang infrastructure
+### Cloud ###
+
+You can join your services made with Ergo Framework into a single cluster with transparent networking using our **Cloud Overlay Network** where they can connect to each other smoothly, no matter where they run - AWS, Azure or GCP, or anywhere else. All these connections are secured with end-to-end encryption. Read more in this article [https://blog.ergo.services/cloud-overlay-network-3a133d47efe5](https://blog.ergo.services/cloud-overlay-network-3a133d47efe5).
+
### Features ###

-* Support Erlang 24 (including [Alias](https://blog.erlang.org/My-OTP-24-Highlights/#eep-53-process-aliases) and [Remote Spawn](https://blog.erlang.org/OTP-23-Highlights/#distributed-spawn-and-the-new-erpc-module) features)
+* Support Erlang 25 (including [Alias](https://blog.erlang.org/My-OTP-24-Highlights/#eep-53-process-aliases) and [Remote Spawn](https://blog.erlang.org/OTP-23-Highlights/#distributed-spawn-and-the-new-erpc-module) features)
* Spawn Erlang-like processes
* Register/unregister processes with simple atom
* `gen.Server` behavior support (with atomic state)
@@ -34,9 +38,9 @@ The goal of this project is to leverage Erlang/OTP experience with Golang perfor
* Permanent
* Temporary
* Transient
-* `gen.Stage` behavior support (originated from Elixir's [GenStage](https://hexdocs.pm/gen_stage/GenStage.html)). This is abstraction built on top of `gen.Server` to provide a simple way to create a distributed Producer/Consumer architecture, while automatically managing the concept of backpressure. This implementation is fully compatible with Elixir's GenStage. Example is here [examples/genstage](examples/genstage) or just run `go run ./examples/genstage` to see it in action
-* `gen.Saga` behavior support. It implements Saga design pattern - a sequence of transactions that updates each service state and publishes the result (or cancels the transaction or triggers the next transaction step). `gen.Saga` also provides a feature of interim results (can be used as transaction progress or as a part of pipeline processing), time deadline (to limit transaction lifespan), two-phase commit (to make distributed transaction atomic). Here is example [examples/gensaga](examples/gensaga).
-* `gen.Raft` behavior support. It's improved implementation of [Raft consensus algorithm](https://raft.github.io). The key improvement is using quorum under the hood to manage the leader election process and make the Raft cluster more reliable. This implementation supports quorums of 3, 5, 7, 9, or 11 quorum members. Here is an example of this feature [examples/genraft](examples/genraft).
+* `gen.Stage` behavior support (originated from Elixir's [GenStage](https://hexdocs.pm/gen_stage/GenStage.html)). This is abstraction built on top of `gen.Server` to provide a simple way to create a distributed Producer/Consumer architecture, while automatically managing the concept of backpressure. This implementation is fully compatible with Elixir's GenStage. Example is here [examples/genstage](https://github.com/ergo-services/examples/tree/master/genstage) or just run `go run ./examples/genstage` to see it in action
+* `gen.Saga` behavior support. It implements Saga design pattern - a sequence of transactions that updates each service state and publishes the result (or cancels the transaction or triggers the next transaction step). `gen.Saga` also provides a feature of interim results (can be used as transaction progress or as a part of pipeline processing), time deadline (to limit transaction lifespan), two-phase commit (to make distributed transaction atomic). Here is example [examples/gensaga](https://github.com/ergo-services/examples/tree/master/gensaga).
+* `gen.Raft` behavior support. It's improved implementation of [Raft consensus algorithm](https://raft.github.io). The key improvement is using quorum under the hood to manage the leader election process and make the Raft cluster more reliable. This implementation supports quorums of 3, 5, 7, 9, or 11 quorum members. Here is an example of this feature [examples/genraft](https://github.com/ergo-services/examples/tree/master/genraft).
* Connect to (accept connection from) any Erlang/Elixir node within a cluster
* Making sync request `ServerProcess.Call`, async - `ServerProcess.Cast` or `Process.Send` in fashion of `gen_server:call`, `gen_server:cast`, `erlang:send` accordingly
* Monitor processes/nodes, local/remote
@@ -63,12 +67,26 @@ Golang introduced [v2 rule](https://go.dev/blog/v2-go-modules) a while ago to so
Here are the changes of latest release. For more details see the [ChangeLog](ChangeLog.md)
+#### [v2.2.1](https://github.com/ergo-services/ergo/releases/tag/v1.999.221) 2023-02-01 [tag version v1.999.221] ####
+
+* Now you can join your services made with Ergo Framework into a single cluster with transparent networking using our **Cloud Overlay Network** where they can connect to each other smoothly, no matter where they run - AWS, Azure or GCP, or anywhere else. All these connections are secured with end-to-end encryption. Read more in this article [https://blog.ergo.services/cloud-overlay-network-3a133d47efe5](https://blog.ergo.services/cloud-overlay-network-3a133d47efe5). Here is an example of this feature in action [examples/cloud](https://github.com/ergo-services/examples/tree/master/cloud)
+* `examples` moved to https://github.com/ergo-services/examples
+* Added support Erlang OTP/25
+* Improved handling `nil` values for the registered types using `etf.RegisterType(...)`
+* Improved self-signed certificate generation
+* Introduced `ergo.debug` option that enables extended debug information for `lib.Log(...)`/`lib.Warning(...)`
+* Fixed `gen.TCP` and `gen.UDP` (missing callbacks)
+* Fixed ETF registering type with `etf.Pid`, `etf.Alias` or `etf.Ref` value types
+* Fixed Cloud client
+* Fixed #117 (incorrect hanshake process finalization)
+* Fixed #139 (panic of the gen.Stage partition dispatcher)
+
#### [v2.2.0](https://github.com/ergo-services/ergo/releases/tag/v1.999.220) 2022-10-18 [tag version v1.999.220] ####
-* Introduced `gen.Web` behavior. It implements **Web API Gateway pattern** is also sometimes known as the "Backend For Frontend" (BFF). See example [examples/genweb](examples/genweb)
-* Introduced `gen.TCP` behavior - **socket acceptor pool for TCP protocols**. It provides everything you need to accept TCP connections and process packets with a small code base and low latency. Here is simple example [examples/gentcp](examples/gentcp)
-* Introduced `gen.UDP` - the same as `gen.TCP`, but for UDP protocols. Example is here [examples/genudp](examples/genudp)
-* Introduced **Events**. This is a simple pub/sub feature within a node - any `gen.Process` can become a producer by registering a new event `gen.Event` using method `gen.Process.RegisterEvent`, while the others can subscribe to these events using `gen.Process.MonitorEvent`. Subscriber process will also receive `gen.MessageEventDown` if a producer process went down (terminated). This feature behaves in a monitor manner but only works within a node. You may also want to subscribe to a system event - `node.EventNetwork` to receive event notification on connect/disconnect any peers. Here is simple example of this feature [examples/events](examples/events)
+* Introduced `gen.Web` behavior. It implements **Web API Gateway pattern** is also sometimes known as the "Backend For Frontend" (BFF). See example [examples/genweb](https://github.com/ergo-services/examples/tree/master/genweb)
+* Introduced `gen.TCP` behavior - **socket acceptor pool for TCP protocols**. It provides everything you need to accept TCP connections and process packets with a small code base and low latency. Here is simple example [examples/gentcp](https://github.com/ergo-services/examples/tree/master/gentcp)
+* Introduced `gen.UDP` - the same as `gen.TCP`, but for UDP protocols. Example is here [examples/genudp](https://github.com/ergo-services/examples/tree/master/genudp)
+* Introduced **Events**. This is a simple pub/sub feature within a node - any `gen.Process` can become a producer by registering a new event `gen.Event` using method `gen.Process.RegisterEvent`, while the others can subscribe to these events using `gen.Process.MonitorEvent`. Subscriber process will also receive `gen.MessageEventDown` if a producer process went down (terminated). This feature behaves in a monitor manner but only works within a node. You may also want to subscribe to a system event - `node.EventNetwork` to receive event notification on connect/disconnect any peers. Here is simple example of this feature [examples/events](https://github.com/ergo-services/examples/tree/master/events)
* Introduced **Cloud Client** - allows connecting to the cloud platform [https://ergo.sevices](https://ergo.services). You may want to register your email there, and we will inform you about the platform launch day
* Introduced **type registration** for the ETF encoding/decoding. This feature allows you to get rid of manually decoding with `etf.TermIntoStruct` for the receiving messages. Register your type using `etf.RegisterType(...)`, and you will be receiving messages in a native type
* Predefined set of errors has moved to the `lib` package
@@ -157,7 +175,7 @@ The one thing that makes embedded EPMD different is the behavior of handling con
### Examples ###
-Code below is a simple implementation of gen.Server pattern [examples/genserver](examples/genserver)
+Code below is a simple implementation of gen.Server pattern [examples/genserver](https://github.com/ergo-services/examples/tree/master/genserver)
```golang
package main
@@ -201,21 +219,22 @@ HandleInfo: 105
exited
```
-See `examples/` for more details
-
-* [gen.Application](examples/application)
-* [gen.Supervisor](examples/supervisor)
-* [gen.Server](examples/genserver)
-* [gen.Stage](examples/genstage)
-* [gen.Saga](examples/gensaga)
-* [gen.Raft](examples/genraft)
-* [gen.Custom](examples/gencustom)
-* [gen.Web](examples/genweb)
-* [gen.TCP](examples/gentcp)
-* [gen.UDP](examples/genudp)
-* [events](examples/events)
-* [erlang](examples/erlang)
-* [proxy](examples/proxy)
+See [https://github.com/ergo-services/examples](https://github.com/ergo-services/examples/) for more details
+
+* [gen.Application](https://github.com/ergo-services/examples/tree/master/application)
+* [gen.Supervisor](https://github.com/ergo-services/examples/tree/master/supervisor)
+* [gen.Server](https://github.com/ergo-services/examples/tree/master/genserver)
+* [gen.Stage](https://github.com/ergo-services/examples/tree/master/genstage)
+* [gen.Saga](https://github.com/ergo-services/examples/tree/master/gensaga)
+* [gen.Raft](https://github.com/ergo-services/examples/tree/master/genraft)
+* [gen.Custom](https://github.com/ergo-services/examples/tree/master/gencustom)
+* [gen.Web](https://github.com/ergo-services/examples/tree/master/genweb)
+* [gen.TCP](https://github.com/ergo-services/examples/tree/master/gentcp)
+* [gen.UDP](https://github.com/ergo-services/examples/tree/master/genudp)
+* [events](https://github.com/ergo-services/examples/tree/master/events)
+* [erlang](https://github.com/ergo-services/examples/tree/master/erlang)
+* [proxy](https://github.com/ergo-services/examples/tree/master/proxy)
+* [cloud](https://github.com/ergo-services/examples/tree/master/cloud)
### Elixir Phoenix Users ###
@@ -244,9 +263,10 @@ func main() {
There are options already defined that you might want to use
-* `-ergo.trace` - enable extended debug info
+* `-ergo.trace` - enable debug info (logging via `lib.Log(...)`)
+* `-ergo.debug` - enable extended debug info (logging via `lib.Log(...)` and `lib.Warning(...)`)
* `-ergo.norecover` - disable panic catching
-* `-ergo.warning` - enable/disable warnings (default: enable)
+* `-ergo.warning` - enable/disable warnings (logging via `lib.Warning(...)`. Default: enable)
To enable Golang profiler just add `--tags debug` in your `go run` or `go build` like this:
diff --git a/apps/cloud/client.go b/apps/cloud/client.go
index 38e08cdd..82696781 100644
--- a/apps/cloud/client.go
+++ b/apps/cloud/client.go
@@ -56,7 +56,7 @@ func (cc *cloudClient) Init(process *gen.ServerProcess, args ...etf.Term) error
handshake: handshake,
}
- if err := process.RegisterEvent(EventCloud, []gen.EventMessage{MessageEventCloud{}}); err != nil {
+ if err := process.RegisterEvent(EventCloud, MessageEventCloud{}); err != nil {
lib.Warning("can't register event %q: %s", EventCloud, err)
}
@@ -95,7 +95,9 @@ func (cc *cloudClient) HandleCast(process *gen.ServerProcess, message etf.Term)
}
}
+ lib.Log("[%s] CLOUD_CLIENT: trying to connect with: %s", process.NodeName(), cloud.Node)
if err := thisNode.Connect(cloud.Node); err != nil {
+ lib.Log("[%s] CLOUD_CLIENT: failed with reason: ", err)
continue
}
@@ -110,9 +112,14 @@ func (cc *cloudClient) HandleCast(process *gen.ServerProcess, message etf.Term)
state.monitor = process.MonitorNode(cloud.Node)
state.node = cloud.Node
event := MessageEventCloud{
- Online: true,
+ Cluster: proxyRoute.Name,
+ Online: true,
+ Proxy: cloud.Node,
+ }
+ if err := process.SendEventMessage(EventCloud, event); err != nil {
+ lib.Log("[%s] CLOUD_CLIENT: failed to send event (%s) %#v: %s",
+ process.NodeName(), EventCloud, event, err)
}
- process.SendEventMessage(EventCloud, event)
return gen.ServerStatusOK
}
@@ -127,7 +134,7 @@ func (cc *cloudClient) HandleInfo(process *gen.ServerProcess, message etf.Term)
state := process.State.(*cloudClientState)
switch m := message.(type) {
- case gen.MessageDown:
+ case gen.MessageNodeDown:
if m.Ref != state.monitor {
return gen.ServerStatusOK
}
@@ -207,10 +214,16 @@ func getCloudNodes() ([]CloudNode, error) {
if err != nil {
return nil, err
}
+
nodes := make([]CloudNode, len(srv))
for i := range srv {
nodes[i].Node = "dist@" + strings.TrimSuffix(srv[i].Target, ".")
nodes[i].Port = srv[i].Port
}
+
+ // return only 3 of them
+ if len(nodes) > 3 {
+ return nodes[:3], nil
+ }
return nodes, nil
}
diff --git a/apps/cloud/handshake.go b/apps/cloud/handshake.go
index 57606b4b..771012a9 100644
--- a/apps/cloud/handshake.go
+++ b/apps/cloud/handshake.go
@@ -102,6 +102,7 @@ func (ch *Handshake) Start(remote net.Addr, conn lib.NetReadWriter, tls bool, co
expectingBytes := 4
await := []byte{ProtoHandshakeV1AuthReply, ProtoHandshakeV1Error}
+ rest := []byte{}
for {
go asyncRead()
@@ -133,11 +134,17 @@ func (ch *Handshake) Start(remote net.Addr, conn lib.NetReadWriter, tls bool, co
return handshake.details, fmt.Errorf("malformed handshake sequence")
}
- await, err = ch.handle(conn, b.B[1], buffer, handshake)
+ await, rest, err = ch.handle(conn, b.B[1], buffer, handshake)
if err != nil {
return handshake.details, err
}
+ if await == nil && rest != nil {
+ // handshaked with some extra data. keep them for the Proto handler
+ handshake.details.Buffer = lib.TakeBuffer()
+ handshake.details.Buffer.Set(rest)
+ }
+
b.Reset()
}
@@ -150,28 +157,29 @@ func (ch *Handshake) Start(remote net.Addr, conn lib.NetReadWriter, tls bool, co
return handshake.details, nil
}
-func (ch *Handshake) handle(socket io.Writer, messageType byte, buffer []byte, details *handshakeDetails) ([]byte, error) {
+func (ch *Handshake) handle(socket io.Writer, messageType byte, buffer []byte, details *handshakeDetails) ([]byte, []byte, error) {
switch messageType {
case ProtoHandshakeV1AuthReply:
if err := ch.handleV1AuthReply(buffer, details); err != nil {
- return nil, err
+ return nil, nil, err
}
if err := ch.sendV1Challenge(socket, details); err != nil {
- return nil, err
+ return nil, nil, err
}
- return []byte{ProtoHandshakeV1ChallengeAccept, ProtoHandshakeV1Error}, nil
+ return []byte{ProtoHandshakeV1ChallengeAccept, ProtoHandshakeV1Error}, nil, nil
case ProtoHandshakeV1ChallengeAccept:
- if err := ch.handleV1ChallegeAccept(buffer, details); err != nil {
- return nil, err
+ rest, err := ch.handleV1ChallegeAccept(buffer, details)
+ if err != nil {
+ return nil, nil, err
}
- return nil, nil
+ return nil, rest, err
case ProtoHandshakeV1Error:
- return nil, ch.handleV1Error(buffer)
+ return nil, nil, ch.handleV1Error(buffer)
default:
- return nil, fmt.Errorf("unknown message type")
+ return nil, nil, fmt.Errorf("unknown message type")
}
}
@@ -234,7 +242,7 @@ func (ch *Handshake) handleV1AuthReply(buffer []byte, handshake *handshakeDetail
digest := GenDigest(handshake.hash, []byte(message.Node), []byte(ch.options.Cluster), handshake.cookieHash)
if bytes.Compare(message.Digest, digest) != 0 {
- return fmt.Errorf("wrong digest")
+ return fmt.Errorf("authorization failed")
}
handshake.digestRemote = digest
handshake.details.Name = message.Node
@@ -243,14 +251,14 @@ func (ch *Handshake) handleV1AuthReply(buffer []byte, handshake *handshakeDetail
return nil
}
-func (ch *Handshake) handleV1ChallegeAccept(buffer []byte, handshake *handshakeDetails) error {
- m, _, err := etf.Decode(buffer, nil, etf.DecodeOptions{})
+func (ch *Handshake) handleV1ChallegeAccept(buffer []byte, handshake *handshakeDetails) ([]byte, error) {
+ m, rest, err := etf.Decode(buffer, nil, etf.DecodeOptions{})
if err != nil {
- return fmt.Errorf("malformed MessageHandshakeV1ChallengeAccept message: %s", err)
+ return nil, fmt.Errorf("malformed MessageHandshakeV1ChallengeAccept message: %s", err)
}
message, ok := m.(MessageHandshakeV1ChallengeAccept)
if ok == false {
- return fmt.Errorf("malformed MessageHandshakeV1ChallengeAccept message: %#v", m)
+ return nil, fmt.Errorf("malformed MessageHandshakeV1ChallengeAccept message: %#v", m)
}
mapping := etf.NewAtomMapping()
@@ -258,7 +266,7 @@ func (ch *Handshake) handleV1ChallegeAccept(buffer []byte, handshake *handshakeD
mapping.Out[etf.Atom(ch.nodename)] = etf.Atom(message.Node)
handshake.details.AtomMapping = mapping
handshake.mapName = message.Node
- return nil
+ return rest, nil
}
func (ch *Handshake) handleV1Error(buffer []byte) error {
diff --git a/apps/cloud/types.go b/apps/cloud/types.go
index ce1271a7..57eee193 100644
--- a/apps/cloud/types.go
+++ b/apps/cloud/types.go
@@ -21,7 +21,9 @@ const (
)
type MessageEventCloud struct {
- Online bool
+ Cluster string
+ Online bool
+ Proxy string
}
func RegisterTypes() error {
diff --git a/etf/cache.go b/etf/cache.go
index 796d0b13..2d1fef32 100644
--- a/etf/cache.go
+++ b/etf/cache.go
@@ -64,8 +64,8 @@ type AtomMapping struct {
}
// NewAtomMapping
-func NewAtomMapping() AtomMapping {
- return AtomMapping{
+func NewAtomMapping() *AtomMapping {
+ return &AtomMapping{
In: make(map[Atom]Atom),
Out: make(map[Atom]Atom),
}
diff --git a/etf/decode.go b/etf/decode.go
index bf8a9c08..4d8e3873 100644
--- a/etf/decode.go
+++ b/etf/decode.go
@@ -23,11 +23,11 @@ type stackElement struct {
}
var (
- termNil = make(List, 0)
-
biggestInt = big.NewInt(0xfffffffffffffff)
lowestInt = big.NewInt(-0xfffffffffffffff)
+ termNil = make(List, 0)
+
errMalformedAtomUTF8 = fmt.Errorf("Malformed ETF. ettAtomUTF8")
errMalformedSmallAtomUTF8 = fmt.Errorf("Malformed ETF. ettSmallAtomUTF8")
errMalformedString = fmt.Errorf("Malformed ETF. ettString")
@@ -60,7 +60,7 @@ var (
// DecodeOptions
type DecodeOptions struct {
- AtomMapping AtomMapping
+ AtomMapping *AtomMapping
FlagBigPidRef bool
}
@@ -130,11 +130,13 @@ func Decode(packet []byte, cache []Atom, options DecodeOptions) (retTerm Term, r
}
// replace atom value if we have mapped value for it
- options.AtomMapping.MutexIn.RLock()
- if mapped, ok := options.AtomMapping.In[atom]; ok {
- atom = mapped
+ if options.AtomMapping != nil {
+ options.AtomMapping.MutexIn.RLock()
+ if mapped, ok := options.AtomMapping.In[atom]; ok {
+ atom = mapped
+ }
+ options.AtomMapping.MutexIn.RUnlock()
}
- options.AtomMapping.MutexIn.RUnlock()
term = atom
packet = packet[n+2:]
@@ -157,11 +159,13 @@ func Decode(packet []byte, cache []Atom, options DecodeOptions) (retTerm Term, r
default:
atom := Atom(packet[1 : n+1])
// replace atom value if we have mapped value for it
- options.AtomMapping.MutexIn.RLock()
- if mapped, ok := options.AtomMapping.In[atom]; ok {
- atom = mapped
+ if options.AtomMapping != nil {
+ options.AtomMapping.MutexIn.RLock()
+ if mapped, ok := options.AtomMapping.In[atom]; ok {
+ atom = mapped
+ }
+ options.AtomMapping.MutexIn.RUnlock()
}
- options.AtomMapping.MutexIn.RUnlock()
term = atom
}
packet = packet[n+1:]
@@ -192,11 +196,13 @@ func Decode(packet []byte, cache []Atom, options DecodeOptions) (retTerm Term, r
default:
atom := cache[int(packet[0])]
// replace atom value if we have mapped value for it
- options.AtomMapping.MutexIn.RLock()
- if mapped, ok := options.AtomMapping.In[atom]; ok {
- atom = mapped
+ if options.AtomMapping != nil {
+ options.AtomMapping.MutexIn.RLock()
+ if mapped, ok := options.AtomMapping.In[atom]; ok {
+ atom = mapped
+ }
+ options.AtomMapping.MutexIn.RUnlock()
}
- options.AtomMapping.MutexIn.RUnlock()
term = atom
}
packet = packet[1:]
@@ -407,7 +413,13 @@ func Decode(packet []byte, cache []Atom, options DecodeOptions) (retTerm Term, r
packet = packet[n+4:]
case ettNil:
- term = termNil
+ // for registered types we should use a nil value
+ // otherwise - treat it as an empty list
+ if stack.reg != nil {
+ term = nil
+ } else {
+ term = termNil
+ }
case ettPid, ettNewPid:
child = &stackElement{
@@ -634,7 +646,16 @@ func Decode(packet []byte, cache []Atom, options DecodeOptions) (retTerm Term, r
set_field = true
field = *stack.reg
}
- stack.term.(Map)[stack.tmp] = term
+ // Erlang can use any value as a key in the map.
+ // OTP 25 sends a message to the 'global_name_server' process
+ // with etf.Tuple as a key in the map, so it caused a panic
+ // and this connection is dropping.
+ switch stack.tmp.(type) {
+ case Tuple:
+ lib.Warning("Erlang sent a etf.Tuple as a map key: %#v => %#v. Ignored this item. Ergo doesn't support it.", stack.tmp, term)
+ default:
+ stack.term.(Map)[stack.tmp] = term
+ }
stack.i++
break
}
@@ -908,12 +929,13 @@ func Decode(packet []byte, cache []Atom, options DecodeOptions) (retTerm Term, r
return nil, nil, errInternal
}
- if set_field {
- if field.Kind() == reflect.Ptr {
- pfield := reflect.New(field.Type().Elem())
- field.Set(pfield)
- field = pfield.Elem()
- }
+ if field.Kind() == reflect.Ptr {
+ pfield := reflect.New(field.Type().Elem())
+ field.Set(pfield)
+ field = pfield.Elem()
+ }
+
+ if set_field && term != nil {
switch field.Kind() {
case reflect.Int8:
switch v := term.(type) {
@@ -947,6 +969,11 @@ func Decode(packet []byte, cache []Atom, options DecodeOptions) (retTerm Term, r
break
}
field.SetInt(int64(v))
+ default:
+ if stack.strict {
+ panic("wrong int8 value")
+ }
+ stack.reg = nil
}
case reflect.Int16:
@@ -981,6 +1008,11 @@ func Decode(packet []byte, cache []Atom, options DecodeOptions) (retTerm Term, r
break
}
field.SetInt(int64(v))
+ default:
+ if stack.strict {
+ panic("wrong int16 value")
+ }
+ stack.reg = nil
}
case reflect.Int32:
@@ -1015,6 +1047,11 @@ func Decode(packet []byte, cache []Atom, options DecodeOptions) (retTerm Term, r
break
}
field.SetInt(int64(v))
+ default:
+ if stack.strict {
+ panic("wrong int32 value")
+ }
+ stack.reg = nil
}
case reflect.Int64:
switch v := term.(type) {
@@ -1032,6 +1069,11 @@ func Decode(packet []byte, cache []Atom, options DecodeOptions) (retTerm Term, r
break
}
field.SetInt(int64(v))
+ default:
+ if stack.strict {
+ panic("wrong int64 value")
+ }
+ stack.reg = nil
}
case reflect.Int:
switch v := term.(type) {
@@ -1052,12 +1094,16 @@ func Decode(packet []byte, cache []Atom, options DecodeOptions) (retTerm Term, r
// overflows
if stack.strict {
panic("overflows int")
- return nil, nil, errMalformed
}
stack.reg = nil
break
}
field.SetInt(int64(v))
+ default:
+ if stack.strict {
+ panic("wrong int value")
+ }
+ stack.reg = nil
}
case reflect.Uint8:
@@ -1092,6 +1138,11 @@ func Decode(packet []byte, cache []Atom, options DecodeOptions) (retTerm Term, r
break
}
field.SetUint(v)
+ default:
+ if stack.strict {
+ panic("wrong uint8 value")
+ }
+ stack.reg = nil
}
case reflect.Uint16:
@@ -1126,6 +1177,11 @@ func Decode(packet []byte, cache []Atom, options DecodeOptions) (retTerm Term, r
break
}
field.SetUint(v)
+ default:
+ if stack.strict {
+ panic("wrong uint16 value")
+ }
+ stack.reg = nil
}
case reflect.Uint32:
switch v := term.(type) {
@@ -1159,6 +1215,11 @@ func Decode(packet []byte, cache []Atom, options DecodeOptions) (retTerm Term, r
break
}
field.SetUint(v)
+ default:
+ if stack.strict {
+ panic("wrong uint32 value")
+ }
+ stack.reg = nil
}
case reflect.Uint64:
@@ -1185,6 +1246,11 @@ func Decode(packet []byte, cache []Atom, options DecodeOptions) (retTerm Term, r
field.SetUint(uint64(v))
case uint64:
field.SetUint(v)
+ default:
+ if stack.strict {
+ panic("wrong uint64 value")
+ }
+ stack.reg = nil
}
case reflect.Uint:
@@ -1219,6 +1285,11 @@ func Decode(packet []byte, cache []Atom, options DecodeOptions) (retTerm Term, r
break
}
field.SetUint(v)
+ default:
+ if stack.strict {
+ panic("wrong uint value")
+ }
+ stack.reg = nil
}
case reflect.Float32:
f, ok := term.(float64)
@@ -1288,6 +1359,9 @@ func Decode(packet []byte, cache []Atom, options DecodeOptions) (retTerm Term, r
default:
if stack.strict {
+ if field.Type().Name() == "Alias" {
+ term = Alias(term.(Ref))
+ }
field.Set(reflect.ValueOf(term))
} else {
// wrap it to catch the panic
@@ -1299,6 +1373,9 @@ func Decode(packet []byte, cache []Atom, options DecodeOptions) (retTerm Term, r
}
}()
}
+ if field.Type().Name() == "Alias" {
+ v = Alias(v.(Ref))
+ }
f.Set(reflect.ValueOf(v))
return true
}
diff --git a/etf/decode_test.go b/etf/decode_test.go
index 1985116e..0aa2c64f 100644
--- a/etf/decode_test.go
+++ b/etf/decode_test.go
@@ -1,7 +1,6 @@
package etf
import (
- "fmt"
"math/big"
"reflect"
"testing"
@@ -417,6 +416,7 @@ func TestDecodeComplex(t *testing.T) {
result := term.(Tuple)
if !reflect.DeepEqual(expected, result) {
+ t.Errorf("got %#v, want %#v", result, expected)
t.Fatal("result != expected")
}
}
@@ -516,7 +516,7 @@ func TestDecodeRegisteredType(t *testing.T) {
}
switch tt := term.(type) {
case regTypeStruct4:
- fmt.Printf("TERM: %v %#v %#v\n", tt, tt, tt.B)
+ //fmt.Printf("TERM: %v %#v %#v\n", tt, tt, tt.B)
default:
t.Fatal("unknown type", tt)
}
diff --git a/etf/encode.go b/etf/encode.go
index 248c999c..a13fe06a 100644
--- a/etf/encode.go
+++ b/etf/encode.go
@@ -30,7 +30,7 @@ type EncodeOptions struct {
AtomCache *AtomCacheOut
SenderAtomCache map[Atom]CacheItem
EncodingAtomCache *EncodingAtomCache
- AtomMapping AtomMapping
+ AtomMapping *AtomMapping
// FlagBigPidRef The node accepts a larger amount of data in pids
// and references (node container types version 4).
@@ -557,11 +557,13 @@ func Encode(term Term, b *lib.Buffer, options EncodeOptions) (retErr error) {
// formats ATOM_UTF8_EXT or SMALL_ATOM_UTF8_EXT.
// replace atom value if we have mapped value for it
- options.AtomMapping.MutexOut.RLock()
- if mapped, ok := options.AtomMapping.Out[t]; ok {
- t = mapped
+ if options.AtomMapping != nil {
+ options.AtomMapping.MutexOut.RLock()
+ if mapped, ok := options.AtomMapping.Out[t]; ok {
+ t = mapped
+ }
+ options.AtomMapping.MutexOut.RUnlock()
}
- options.AtomMapping.MutexOut.RUnlock()
// https://erlang.org/doc/apps/erts/erl_ext_dist.html#utf8_atoms
// The maximum number of allowed characters in an atom is 255.
diff --git a/etf/etf.go b/etf/etf.go
index 3ecb4c62..5a2d79c3 100644
--- a/etf/etf.go
+++ b/etf/etf.go
@@ -126,14 +126,13 @@ type Ref struct {
// Marshaler interface implemented by types that can marshal themselves into valid ETF binary
// Interface implementation must be over the object e.g. (MyObject) UnmarshalETF:
//
-// type MyObject struct{}
-//
-// func (m MyObject) MarshalETF() ([]byte, error) {
-// var encoded []byte
-// ... encoding routine ...
-// return encoded, nil
-// }
+// type MyObject struct{}
//
+// func (m MyObject) MarshalETF() ([]byte, error) {
+// var encoded []byte
+// ... encoding routine ...
+// return encoded, nil
+// }
type Marshaler interface {
MarshalETF() ([]byte, error)
}
@@ -142,13 +141,13 @@ type Marshaler interface {
// Returns error ErrEmpty for []byte{}.
// Interface implementation must be over pointer to the object e.g. (*MyObject) UnmarshalETF:
//
-// type MyObject struct{}
+// type MyObject struct{}
//
-// func (m *MyObject) UnmarshalETF(b []byte) error {
-// var err error
-// ... decoding routine ...
-// return err
-// }
+// func (m *MyObject) UnmarshalETF(b []byte) error {
+// var err error
+// ... decoding routine ...
+// return err
+// }
type Unmarshaler interface {
UnmarshalETF([]byte) error
}
@@ -669,6 +668,10 @@ type RegisterTypeOptions struct {
// for unregistering this type.
// Returns an error if this type can not be registered.
func RegisterType(t interface{}, options RegisterTypeOptions) (Atom, error) {
+ switch t.(type) {
+ case Pid, Ref, Alias:
+ return "", fmt.Errorf("types Pid, Ref, Alias can not be registered")
+ }
tt := reflect.TypeOf(t)
ttk := tt.Kind()
@@ -736,6 +739,12 @@ func RegisterType(t interface{}, options RegisterTypeOptions) (Atom, error) {
return name, fmt.Errorf("struct has unexported field(s)")
}
+ switch f.Interface().(type) {
+ case Pid, Ref, Alias:
+ // ignore this types
+ continue
+ }
+
if f.Type().Kind() == reflect.Slice && f.Type().Elem().Kind() == reflect.Uint8 {
// []byte
continue
diff --git a/etf/etf_test.go b/etf/etf_test.go
index 6cb9ebe3..e92836b3 100644
--- a/etf/etf_test.go
+++ b/etf/etf_test.go
@@ -605,6 +605,10 @@ func TestRegisterType(t *testing.T) {
C float32
C64 float64
D ddd
+
+ T1 Pid
+ T2 Ref
+ T3 Alias
}
type bbb map[aaa]ccc
@@ -623,7 +627,11 @@ func TestRegisterType(t *testing.T) {
BU64: 0x48c,
C: -11.22,
C64: 1164.22,
- D: ddd{true, false, false}}: ccc{"a1", "a2", "a3"},
+ D: ddd{true, false, false},
+ T1: Pid{Node: Atom("nodepid11"), ID: 123, Creation: 456},
+ T2: Ref{Node: Atom("noderef11"), Creation: 123, ID: [5]uint32{4, 5, 6, 0, 0}},
+ T3: Alias{Node: Atom("nodealias11"), Creation: 123, ID: [5]uint32{4, 5, 6, 0, 0}},
+ }: ccc{"a1", "a2", "a3"},
aaa{
A: "bb",
B: -22,
@@ -638,7 +646,11 @@ func TestRegisterType(t *testing.T) {
BU64: 0x8d8,
C: -22.22,
C64: 2264.22,
- D: ddd{false, true, false}}: ccc{"b1", "b2", "b3"},
+ D: ddd{false, true, false},
+ T1: Pid{Node: Atom("nodepid22"), ID: 123, Creation: 456},
+ T2: Ref{Node: Atom("noderef22"), Creation: 123, ID: [5]uint32{4, 5, 6, 0, 0}},
+ T3: Alias{Node: Atom("nodealias22"), Creation: 123, ID: [5]uint32{4, 5, 6, 0, 0}},
+ }: ccc{"b1", "b2", "b3"},
aaa{
A: "cc",
B: -33,
@@ -653,12 +665,26 @@ func TestRegisterType(t *testing.T) {
BU64: 0xd24,
C: -33.22,
C64: 3364.22,
- D: ddd{false, false, true}}: ccc{}, //test empty list
+ D: ddd{false, false, true},
+ T1: Pid{Node: Atom("nodepid33"), ID: 123, Creation: 456},
+ T2: Ref{Node: Atom("noderef33"), Creation: 123, ID: [5]uint32{4, 5, 6, 0, 0}},
+ T3: Alias{Node: Atom("nodealias33"), Creation: 123, ID: [5]uint32{4, 5, 6, 0, 0}},
+ }: ccc{}, //test empty list
}
buf := lib.TakeBuffer()
defer lib.ReleaseBuffer(buf)
+ if _, err := RegisterType(Pid{}, RegisterTypeOptions{Strict: true}); err == nil {
+ t.Fatal("shouldn't be registered")
+ }
+ if _, err := RegisterType(Ref{}, RegisterTypeOptions{Strict: true}); err == nil {
+ t.Fatal("shouldn't be registered")
+ }
+ if _, err := RegisterType(Alias{}, RegisterTypeOptions{Strict: true}); err == nil {
+ t.Fatal("shouldn't be registered")
+ }
+
if _, err := RegisterType(ddd{}, RegisterTypeOptions{Strict: true}); err != nil {
t.Fatal(err)
}
@@ -672,10 +698,17 @@ func TestRegisterType(t *testing.T) {
t.Fatal(err)
}
- if err := Encode(src, buf, EncodeOptions{}); err != nil {
+ encodeOptions := EncodeOptions{
+ FlagBigPidRef: true,
+ FlagBigCreation: true,
+ }
+ if err := Encode(src, buf, encodeOptions); err != nil {
t.Fatal(err)
}
- dst, _, err := Decode(buf.B, []Atom{}, DecodeOptions{})
+ decodeOptions := DecodeOptions{
+ FlagBigPidRef: true,
+ }
+ dst, _, err := Decode(buf.B, []Atom{}, decodeOptions)
if err != nil {
t.Fatal(err)
}
@@ -685,6 +718,6 @@ func TestRegisterType(t *testing.T) {
}
if !reflect.DeepEqual(src, dst) {
- t.Errorf("got %v, want %v", dst, src)
+ t.Errorf("got:\n%#v\n\nwant:\n%#v\n", dst, src)
}
}
diff --git a/examples/application/README.md b/examples/application/README.md
deleted file mode 100644
index 9bfda676..00000000
--- a/examples/application/README.md
+++ /dev/null
@@ -1,45 +0,0 @@
-## Application demo scenario ##
-
-This example implements a simple application that starts one server and one supervisor. Here is supervision tree of this application
-
-```
- demoApp
- |
- - demoSup
- | |
- | - demoServer01
- | - demoServer02
- | - demoServer03
- |
- - demoServer
-```
-
-Here is output of this example:
-```
-❯❯❯❯ go run .
-
-to stop press Ctrl-C
-
-Started new process
- Pid: <1A1F7F53.0.1013>
- Name: "demoServer01"
- Parent: <1A1F7F53.0.1012>
- Args:[]etf.Term(nil)
-Started new process
- Pid: <1A1F7F53.0.1014>
- Name: "demoServer02"
- Parent: <1A1F7F53.0.1012>
- Args:[]etf.Term{12345}
-Started new process
- Pid: <1A1F7F53.0.1015>
- Name: "demoServer03"
- Parent: <1A1F7F53.0.1012>
- Args:[]etf.Term{"abc", 67890}
-Started new process
- Pid: <1A1F7F53.0.1016>
- Name: "demoServer"
- Parent: <1A1F7F53.0.1011>
- Args:[]etf.Term(nil)
-Application started with Pid <1A1F7F53.0.1011>!
-
-```
diff --git a/examples/application/app.go b/examples/application/app.go
deleted file mode 100644
index 6895a50e..00000000
--- a/examples/application/app.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-func createDemoApp() gen.ApplicationBehavior {
- return &demoApp{}
-}
-
-type demoApp struct {
- gen.Application
-}
-
-func (da *demoApp) Load(args ...etf.Term) (gen.ApplicationSpec, error) {
- return gen.ApplicationSpec{
- Name: "demoApp",
- Description: "Demo Applicatoin",
- Version: "v.1.0",
- Children: []gen.ApplicationChildSpec{
- gen.ApplicationChildSpec{
- Child: createDemoSup(),
- Name: "demoSup",
- },
- gen.ApplicationChildSpec{
- Child: createDemoServer(),
- Name: "demoServer",
- },
- },
- }, nil
-}
-
-func (da *demoApp) Start(process gen.Process, args ...etf.Term) {
- fmt.Printf("Application started with Pid %s!\n", process.Self())
-}
diff --git a/examples/application/main.go b/examples/application/main.go
deleted file mode 100644
index fd38c831..00000000
--- a/examples/application/main.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
-
- "github.com/ergo-services/ergo"
- "github.com/ergo-services/ergo/gen"
- "github.com/ergo-services/ergo/node"
-)
-
-func main() {
- flag.Parse()
-
- fmt.Println("")
- fmt.Println("to stop press Ctrl-C")
- fmt.Println("")
-
- apps := []gen.ApplicationBehavior{
- createDemoApp(),
- }
- opts := node.Options{
- Applications: apps,
- }
- demoNode, err := ergo.StartNode("app@localhost", "cookie", opts)
- if err != nil {
- panic(err)
- }
- demoNode.Wait()
-}
diff --git a/examples/application/server.go b/examples/application/server.go
deleted file mode 100644
index 22da0f07..00000000
--- a/examples/application/server.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-func createDemoServer() gen.ServerBehavior {
- return &demoServer{}
-}
-
-type demoServer struct {
- gen.Server
-}
-
-func (ds *demoServer) Init(process *gen.ServerProcess, args ...etf.Term) error {
- fmt.Printf("Started new process\n\tPid: %s\n\tName: %q\n\tParent: %s\n\tArgs:%#v\n",
- process.Self(),
- process.Name(),
- process.Parent().Self(),
- args)
- return nil
-}
diff --git a/examples/application/sup.go b/examples/application/sup.go
deleted file mode 100644
index ae9f9d70..00000000
--- a/examples/application/sup.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package main
-
-import (
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-func createDemoSup() gen.SupervisorBehavior {
- return &demoSup{}
-}
-
-type demoSup struct {
- gen.Supervisor
-}
-
-func (ds *demoSup) Init(args ...etf.Term) (gen.SupervisorSpec, error) {
- spec := gen.SupervisorSpec{
- Name: "demoAppSup",
- Children: []gen.SupervisorChildSpec{
- gen.SupervisorChildSpec{
- Name: "demoServer01",
- Child: createDemoServer(),
- },
- gen.SupervisorChildSpec{
- Name: "demoServer02",
- Child: createDemoServer(),
- Args: []etf.Term{12345},
- },
- gen.SupervisorChildSpec{
- Name: "demoServer03",
- Child: createDemoServer(),
- Args: []etf.Term{"abc", 67890},
- },
- },
- Strategy: gen.SupervisorStrategy{
- Type: gen.SupervisorStrategyOneForAll,
- Intensity: 2,
- Period: 5,
- Restart: gen.SupervisorStrategyRestartTemporary,
- },
- }
- return spec, nil
-}
diff --git a/examples/erlang/README.md b/examples/erlang/README.md
deleted file mode 100644
index ae2f6339..00000000
--- a/examples/erlang/README.md
+++ /dev/null
@@ -1,7 +0,0 @@
-## Erlang demo scenario ##
-
-This example demonstrates how Ergo's node interop with the Erlang node.
-
-Here is output of this demonstration
-
-
diff --git a/examples/erlang/main.go b/examples/erlang/main.go
deleted file mode 100644
index 9bbbd9d0..00000000
--- a/examples/erlang/main.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
-
- "github.com/ergo-services/ergo"
- "github.com/ergo-services/ergo/gen"
- "github.com/ergo-services/ergo/node"
-)
-
-var (
- ServerName string
- NodeName string
- Cookie string
-)
-
-func init() {
- flag.StringVar(&ServerName, "server", "example", "server process name")
- flag.StringVar(&NodeName, "name", "demo@127.0.0.1", "node name")
- flag.StringVar(&Cookie, "cookie", "123", "cookie for interaction with erlang cluster")
-}
-
-func main() {
- flag.Parse()
-
- fmt.Println("")
- fmt.Println("to stop press Ctrl-C")
- fmt.Println("")
-
- node, err := ergo.StartNode(NodeName, Cookie, node.Options{})
- if err != nil {
- panic(err)
- }
-
- _, err = node.Spawn(ServerName, gen.ProcessOptions{}, &demo{})
- if err != nil {
- panic(err)
- }
-
- fmt.Println("Start erlang node with the command below:")
- fmt.Printf(" $ erl -name %s -setcookie %s\n\n", "erl-"+node.Name(), Cookie)
-
- fmt.Println("----- to make call request from 'erl'-shell:")
- fmt.Printf("gen_server:call({%s,'%s'}, hi).\n", ServerName, NodeName)
- fmt.Printf("gen_server:call({%s,'%s'}, {echo, 1,2,3}).\n", ServerName, NodeName)
- fmt.Println("----- to send cast message from 'erl'-shell:")
- fmt.Printf("gen_server:cast({%s,'%s'}, {cast, 1,2,3}).\n", ServerName, NodeName)
- fmt.Println("----- send atom 'stop' to stop server :")
- fmt.Printf("gen_server:cast({%s,'%s'}, stop).\n", ServerName, NodeName)
-
- node.Wait()
-}
diff --git a/examples/erlang/server.go b/examples/erlang/server.go
deleted file mode 100644
index 73260116..00000000
--- a/examples/erlang/server.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-type demo struct {
- gen.Server
-}
-
-func (d *demo) HandleCast(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
- fmt.Printf("[%s] HandleCast: %#v\n", process.Name(), message)
- switch message {
- case etf.Atom("stop"):
- return gen.ServerStatusStopWithReason("stop they said")
- }
- return gen.ServerStatusOK
-}
-
-func (d *demo) HandleCall(process *gen.ServerProcess, from gen.ServerFrom, message etf.Term) (etf.Term, gen.ServerStatus) {
- fmt.Printf("[%s] HandleCall: %#v, From: %s\n", process.Name(), message, from.Pid)
-
- switch message.(type) {
- case etf.Atom:
- return "hello", gen.ServerStatusOK
-
- default:
- return message, gen.ServerStatusOK
- }
-}
-
-func (d *demo) Terminate(process *gen.ServerProcess, reason string) {
- fmt.Printf("[%s] Terminating process with reason %q", process.Name(), reason)
-}
diff --git a/examples/events/README.md b/examples/events/README.md
deleted file mode 100644
index 09600cf1..00000000
--- a/examples/events/README.md
+++ /dev/null
@@ -1,26 +0,0 @@
-## Events demo scenario ##
-
-This example implements simple publisher and subscriber to demonstrate `gen.Event` feature.
-
-Here is output of this example:
-
-```
-❯❯❯❯ go run .
-Start node eventsnode@localhost
-process <91BC521D.0.1011> registered event simple
-Started process <91BC521D.0.1011> with name "producer"
-Started process <91BC521D.0.1012> with name "consumer"
-... producing event: {EVNT 1}
-consumer got event: {EVNT 1}
-... producing event: {EVNT 2}
-consumer got event: {EVNT 2}
-... producing event: {EVNT 3}
-consumer got event: {EVNT 3}
-... producing event: {EVNT 4}
-consumer got event: {EVNT 4}
-... producing event: {EVNT 5}
-consumer got event: {EVNT 5}
-producer has terminated
-Stop node eventsnode@localhost
-
-```
diff --git a/examples/events/consumer.go b/examples/events/consumer.go
deleted file mode 100644
index eccb27b0..00000000
--- a/examples/events/consumer.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-type consumer struct {
- gen.Server
-}
-
-func (c *consumer) Init(process *gen.ServerProcess, args ...etf.Term) error {
- if err := process.MonitorEvent(simpleEvent); err != nil {
- return err
- }
- return nil
-}
-
-func (c *consumer) HandleInfo(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
- switch message.(type) {
- case messageSimpleEvent:
- fmt.Println("consumer got event: ", message)
- case gen.MessageEventDown:
- fmt.Println("producer has terminated")
- return gen.ServerStatusStop
- default:
- fmt.Println("unknown message", message)
- }
- return gen.ServerStatusOK
-}
diff --git a/examples/events/main.go b/examples/events/main.go
deleted file mode 100644
index e882be22..00000000
--- a/examples/events/main.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
-
- "github.com/ergo-services/ergo"
- "github.com/ergo-services/ergo/gen"
- "github.com/ergo-services/ergo/node"
-)
-
-const (
- simpleEvent gen.Event = "simple"
-)
-
-type messageSimpleEvent struct {
- e string
-}
-
-func main() {
- flag.Parse()
-
- fmt.Println("Start node eventsnode@localhost")
- myNode, _ := ergo.StartNode("eventsnode@localhost", "cookies", node.Options{})
-
- prod, _ := myNode.Spawn("producer", gen.ProcessOptions{}, &producer{})
- fmt.Printf("Started process %s with name %q\n", prod.Self(), prod.Name())
-
- cons, _ := myNode.Spawn("consumer", gen.ProcessOptions{}, &consumer{})
- fmt.Printf("Started process %s with name %q\n", cons.Self(), cons.Name())
-
- cons.Wait()
- fmt.Println("Stop node", myNode.Name())
- myNode.Stop()
-}
diff --git a/examples/events/producer.go b/examples/events/producer.go
deleted file mode 100644
index bf3e1aaf..00000000
--- a/examples/events/producer.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
- "github.com/ergo-services/ergo/lib"
-)
-
-type producer struct {
- gen.Server
-}
-
-func (p *producer) Init(process *gen.ServerProcess, args ...etf.Term) error {
-
- if err := process.RegisterEvent(simpleEvent, messageSimpleEvent{}); err != nil {
- lib.Warning("can't register event %q: %s", simpleEvent, err)
- }
- fmt.Printf("process %s registered event %s\n", process.Self(), simpleEvent)
- process.SendAfter(process.Self(), 1, time.Second)
- return nil
-}
-
-func (p *producer) HandleInfo(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
- n := message.(int)
- if n > 5 {
- return gen.ServerStatusStop
- }
- // sending message with delay 1 second
- process.SendAfter(process.Self(), n+1, time.Second)
- event := messageSimpleEvent{
- e: fmt.Sprintf("EVNT %d", n),
- }
-
- fmt.Printf("... producing event: %s\n", event)
- if err := process.SendEventMessage(simpleEvent, event); err != nil {
- fmt.Println("can't send event:", err)
- }
- return gen.ServerStatusOK
-}
diff --git a/examples/gencustom/README.md b/examples/gencustom/README.md
deleted file mode 100644
index 433a31ec..00000000
--- a/examples/gencustom/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-## GenCustom ##
-
-This example demonstrates how to create a custom behavior (design pattern) on top of the gen.Server.
-
-Take inspiration from the `gen/stage.go` or `gen/saga.go` design patterns.
diff --git a/examples/gencustom/gen_custom.go b/examples/gencustom/gen_custom.go
deleted file mode 100644
index 695a96ac..00000000
--- a/examples/gencustom/gen_custom.go
+++ /dev/null
@@ -1,144 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-type Custom struct {
- gen.Server
-}
-
-type CustomProcess struct {
- gen.ServerProcess
- counter int
-}
-
-// CustomBehavior interface
-type CustomBehavior interface {
- //
- // Mandatory callbacks
- //
-
- // InitCustom
- InitCustom(process *CustomProcess, args ...etf.Term) error
-
- // HandleHello invoked on a 'hello'
- HandleHello(process *CustomProcess) CustomStatus
-
- //
- // Optional callbacks
- //
-
- // HandleCustomCall this callback is invoked on ServerProcess.Call. This method is optional
- // for the implementation
- HandleCustomCall(process *CustomProcess, from gen.ServerFrom, message etf.Term) (etf.Term, gen.ServerStatus)
- // HandleCustomDirect this callback is invoked on Process.Direct. This method is optional
- // for the implementation
- HandleCustomDirect(process *CustomProcess, message interface{}) (interface{}, error)
- // HandleCustomCast this callback is invoked on ServerProcess.Cast. This method is optional
- // for the implementation
- HandleCustomCast(process *CustomProcess, message etf.Term) gen.ServerStatus
- // HandleCustomInfo this callback is invoked on Process.Send. This method is optional
- // for the implementation
- HandleCustomInfo(process *CustomProcess, message etf.Term) gen.ServerStatus
-}
-
-type CustomStatus error
-
-var (
- CustomStatusOK CustomStatus = nil
- CustomStatusStop CustomStatus = fmt.Errorf("stop")
-)
-
-// default Custom callbacks
-
-func (gd *Custom) HandleCustomCall(process *CustomProcess, from gen.ServerFrom, message etf.Term) (etf.Term, gen.ServerStatus) {
- fmt.Printf("HandleCustomCall: unhandled message (from %#v) %#v\n", from, message)
- return etf.Atom("ok"), gen.ServerStatusOK
-}
-
-func (gd *Custom) HandleCustomCast(process *CustomProcess, message etf.Term) gen.ServerStatus {
- fmt.Printf("HandleCustomCast: unhandled message %#v\n", message)
- return gen.ServerStatusOK
-}
-func (gd *Custom) HandleCustomInfo(process *CustomProcess, message etf.Term) gen.ServerStatus {
- fmt.Printf("HandleCustomInfo: unhandled message %#v\n", message)
- return gen.ServerStatusOK
-}
-
-//
-// API
-//
-
-type messageHello struct{}
-
-func (d *Custom) Hello(process gen.Process) error {
- _, err := process.Direct(messageHello{})
- return err
-}
-
-type messageGetStat struct{}
-
-func (d *Custom) Stat(process gen.Process) int {
- counter, _ := process.Direct(messageGetStat{})
- return counter.(int)
-}
-
-//
-// Custom methods. Available to use inside actors only.
-//
-
-func (dp *CustomProcess) Hi() CustomStatus {
- dp.counter = dp.counter * 2
- return CustomStatusOK
-}
-
-//
-// gen.Server callbacks
-//
-func (d *Custom) Init(process *gen.ServerProcess, args ...etf.Term) error {
- custom := &CustomProcess{
- ServerProcess: *process,
- }
- // do not inherit parent State
- custom.State = nil
-
- if err := process.Behavior().(CustomBehavior).InitCustom(custom, args...); err != nil {
- return err
- }
- process.State = custom
- return nil
-}
-
-func (gd *Custom) HandleCall(process *gen.ServerProcess, from gen.ServerFrom, message etf.Term) (etf.Term, gen.ServerStatus) {
- custom := process.State.(*CustomProcess)
- return process.Behavior().(CustomBehavior).HandleCustomCall(custom, from, message)
-}
-
-func (gd *Custom) HandleDirect(process *gen.ServerProcess, ref etf.Ref, message interface{}) (interface{}, gen.DirectStatus) {
- custom := process.State.(*CustomProcess)
- switch message.(type) {
- case messageGetStat:
- return custom.counter, gen.DirectStatusOK
- case messageHello:
- process.Behavior().(CustomBehavior).HandleHello(custom)
- custom.counter++
- return nil, gen.DirectStatusOK
- default:
- return process.Behavior().(CustomBehavior).HandleCustomDirect(custom, message)
- }
-
-}
-
-func (gd *Custom) HandleCast(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
- custom := process.State.(*CustomProcess)
- return process.Behavior().(CustomBehavior).HandleCustomCast(custom, message)
-}
-
-func (gd *Custom) HandleInfo(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
- custom := process.State.(*CustomProcess)
- return process.Behavior().(CustomBehavior).HandleCustomInfo(custom, message)
-}
diff --git a/examples/gencustom/main.go b/examples/gencustom/main.go
deleted file mode 100644
index 66948169..00000000
--- a/examples/gencustom/main.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-
- "github.com/ergo-services/ergo"
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
- "github.com/ergo-services/ergo/node"
-)
-
-type MyCustom struct {
- Custom
-}
-
-func (md *MyCustom) InitCustom(process *CustomProcess, args ...etf.Term) error {
- fmt.Printf("Started instance of MyCustom with PID %s and args %v\n", process.Self(), args)
- return nil
-}
-
-func (md *MyCustom) HandleHello(process *CustomProcess) CustomStatus {
- fmt.Println("got Hello")
- return CustomStatusOK
-}
-
-func (md *MyCustom) HandleCustomDirect(process *CustomProcess, message interface{}) (interface{}, error) {
-
- fmt.Println("Say hi to increase counter twice")
- process.Hi()
- return nil, nil
-}
-
-func main() {
-
- // Initialize new node with given name, cookie, listening port range and epmd port
- node, e := ergo.StartNode("mydemo@127.0.0.1", "cookie", node.Options{})
- if e != nil {
- fmt.Println("error", e)
- return
- }
-
- demo := &MyCustom{}
- // Spawn a new process with arguments
- process, e := node.Spawn("demo", gen.ProcessOptions{}, demo, 1, 2, 3)
- if e != nil {
- fmt.Println("error", e)
- return
- }
-
- fmt.Println("call Hello method 3 times")
- demo.Hello(process)
- demo.Hello(process)
- demo.Hello(process)
-
- fmt.Println("How many times Hello was called: ", demo.Stat(process))
-
- fmt.Println("make direct request")
- process.Direct(nil)
- fmt.Println("How big is the counter now: ", demo.Stat(process))
-
- fmt.Println("send a simple message (no handler) and call Exit")
- process.Send(process.Self(), "simple message")
-
- // add some delay to see "unhandled message" warning
- time.Sleep(100 * time.Millisecond)
-
- fmt.Println("Exiting")
- process.Exit("normal")
- process.Wait()
- node.Stop()
- node.Wait()
- return
-}
diff --git a/examples/genraft/README.md b/examples/genraft/README.md
deleted file mode 100644
index 6a831e7f..00000000
--- a/examples/genraft/README.md
+++ /dev/null
@@ -1,57 +0,0 @@
-## Raft demo scenario ##
-
-1. Starting 4 nodes with raft processes. They are connecting to each other to build a quorum. Since we use 4 raft processes, only 3 of them will be chosen for the quorum (gen.RaftQuorumState3). One becomes a follower (it won't receive the leader election result).
-2. Quorum is built. All cluster members receive infomartion about the new quorum (invoking HandleQuorum callback on each raft process). Quorum members start leader election.
-3. Leader is elected (chose the raft process with the latest 'serial'). Every quorum member receives information about the leader and its 'serial' (invoking HandleLeader callback on them).
-4. Raft process checks the missing serials on its storage and makes a Get request to the cluster to receive them.
-5. Follower makes an Append request to add a new key/value to the cluster (it sends to the quorum member, and the quorum member forwards it further to the leader)
-6. All cluster members receive the new key/value with the increased 'serial' by 1.
-7. Raft process checks the missing serials on its storage and makes a Get request to the cluster to receive them (follower had no chance to make this check since it doesn't receive information with the election leader result)
-
-Here is output of this example
-
-```
-❯❯❯❯ go run .
-
-to stop press Ctrl-C or wait 10 seconds...
-
-Starting node: node1@localhost and raft1 process - Serial: 1 PID: <1B30E9C5.0.1008> ...OK
-Starting node: node2@localhost and raft2 process - Serial: 4 PID: <08151198.0.1008> ...OK
-Starting node: node3@localhost and raft3 process - Serial: 4 PID: <9FF54552.0.1008> ...OK
-Starting node: node4@localhost and raft4 process - Serial: 0 PID: <2E5EE122.0.1008> ...OK
-<08151198.0.1008> Quorum built - State: 3 Quorum member: true
-<9FF54552.0.1008> Quorum built - State: 3 Quorum member: true
-<2E5EE122.0.1008> Quorum built - State: 3 Quorum member: false
-<2E5EE122.0.1008> since I'm not a quorum member, I won't receive any information about elected leader
-<1B30E9C5.0.1008> Quorum built - State: 3 Quorum member: true
-<08151198.0.1008> I'm a leader of this quorum
-<9FF54552.0.1008> Leader elected: <08151198.0.1008> with serial 4
-<1B30E9C5.0.1008> Leader elected: <08151198.0.1008> with serial 4
-<1B30E9C5.0.1008> Missing serials: 2 .. 4
-<1B30E9C5.0.1008> requested missing serial to the cluster: 2 id: Ref#<1B30E9C5.138519.23452.0>
-<1B30E9C5.0.1008> requested missing serial to the cluster: 3 id: Ref#<1B30E9C5.138520.23452.0>
-<1B30E9C5.0.1008> requested missing serial to the cluster: 4 id: Ref#<1B30E9C5.138521.23452.0>
-<08151198.0.1008> Received request for serial 2
-<08151198.0.1008> Received request for serial 3
-<08151198.0.1008> Received request for serial 4
-<1B30E9C5.0.1008> Received requested serial 2 with key key2 and value value2
-<1B30E9C5.0.1008> Received requested serial 3 with key key3 and value value3
-<1B30E9C5.0.1008> Received requested serial 4 with key key4 and value value4
-<08151198.0.1008> Received append request with serial 5, key "key100" and value "value100"
-<2E5EE122.0.1008> Received append request with serial 5, key "key100" and value "value100"
-<1B30E9C5.0.1008> Received append request with serial 5, key "key100" and value "value100"
-<2E5EE122.0.1008> Missing serial: 1 requested missing serial to the cluster. id Ref#<2E5EE122.40367.23452.0>
-<2E5EE122.0.1008> Missing serial: 2 requested missing serial to the cluster. id Ref#<2E5EE122.40368.23452.0>
-<2E5EE122.0.1008> Missing serial: 3 requested missing serial to the cluster. id Ref#<2E5EE122.40369.23452.0>
-<2E5EE122.0.1008> Missing serial: 4 requested missing serial to the cluster. id Ref#<2E5EE122.40370.23452.0>
-<9FF54552.0.1008> Received append request with serial 5, key "key100" and value "value100"
-<1B30E9C5.0.1008> Received request for serial 1
-<08151198.0.1008> Received request for serial 2
-<08151198.0.1008> Received request for serial 3
-<08151198.0.1008> Received request for serial 4
-<2E5EE122.0.1008> Received requested serial 1 with key key1 and value value1
-<2E5EE122.0.1008> Received requested serial 2 with key key2 and value value2
-<2E5EE122.0.1008> Received requested serial 3 with key key3 and value value3
-<2E5EE122.0.1008> Received requested serial 4 with key key4 and value value4
-
-```
diff --git a/examples/genraft/main.go b/examples/genraft/main.go
deleted file mode 100644
index 8088643a..00000000
--- a/examples/genraft/main.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-
- "github.com/ergo-services/ergo"
- "github.com/ergo-services/ergo/gen"
- "github.com/ergo-services/ergo/node"
-)
-
-func main() {
-
- fmt.Println("")
- fmt.Println("to stop press Ctrl-C or wait 10 seconds...")
- fmt.Println("")
-
- fmt.Printf("Starting node: node1@localhost and raft1 process - ")
- node1, err := ergo.StartNode("node1@localhost", "cookies", node.Options{})
- if err != nil {
- panic(err)
- }
- defer node1.Stop()
- raft1 := &Raft{startSerial: 1}
- _, err = node1.Spawn("raft1", gen.ProcessOptions{}, raft1)
- if err != nil {
- panic(err)
- }
- fmt.Println("OK")
-
- fmt.Printf("Starting node: node2@localhost and raft2 process - ")
- node2, err := ergo.StartNode("node2@localhost", "cookies", node.Options{})
- if err != nil {
- panic(err)
- }
- defer node2.Stop()
- raft2 := &Raft{
- startSerial: 4,
- startPeers: []gen.ProcessID{gen.ProcessID{Name: "raft1", Node: "node1@localhost"}},
- }
- _, err = node2.Spawn("raft2", gen.ProcessOptions{}, raft2)
- if err != nil {
- panic(err)
- }
- fmt.Println("OK")
-
- fmt.Printf("Starting node: node3@localhost and raft3 process - ")
- node3, err := ergo.StartNode("node3@localhost", "cookies", node.Options{})
- if err != nil {
- panic(err)
- }
- defer node3.Stop()
- raft3 := &Raft{
- startSerial: 4,
- startPeers: []gen.ProcessID{gen.ProcessID{Name: "raft1", Node: "node1@localhost"}},
- }
- _, err = node3.Spawn("raft3", gen.ProcessOptions{}, raft3)
- if err != nil {
- panic(err)
- }
- fmt.Println("OK")
-
- fmt.Printf("Starting node: node4@localhost and raft4 process - ")
- node4, err := ergo.StartNode("node4@localhost", "cookies", node.Options{})
- if err != nil {
- panic(err)
- }
- defer node4.Stop()
- raft4 := &Raft{
- startSerial: 0,
- startPeers: []gen.ProcessID{gen.ProcessID{Name: "raft1", Node: "node1@localhost"}},
- }
- _, err = node4.Spawn("raft4", gen.ProcessOptions{}, raft4)
- if err != nil {
- panic(err)
- }
- fmt.Println("OK")
- time.Sleep(10 * time.Second)
-}
diff --git a/examples/genraft/raft.go b/examples/genraft/raft.go
deleted file mode 100644
index 2fdc7633..00000000
--- a/examples/genraft/raft.go
+++ /dev/null
@@ -1,113 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-//
-// Raft
-//
-type Raft struct {
- gen.Raft
- storage *storage
-
- startSerial uint64
- startPeers []gen.ProcessID
-}
-
-type messageAppend struct {
- key string
- value string
-}
-
-func (r *Raft) InitRaft(process *gen.RaftProcess, args ...etf.Term) (gen.RaftOptions, error) {
- opts := gen.RaftOptions{
- Serial: r.startSerial,
- Peers: r.startPeers,
- }
-
- r.storage = &storage{}
- r.storage.Init(int(opts.Serial))
- fmt.Printf("Serial: %d PID: %s ...", opts.Serial, process.Self())
-
- return opts, nil
-}
-func (r *Raft) HandleQuorum(process *gen.RaftProcess, quorum *gen.RaftQuorum) gen.RaftStatus {
- fmt.Println(process.Self(), "Quorum built - State:", quorum.State, "Quorum member:", quorum.Member)
- if quorum.Member == false {
- fmt.Println(process.Self(), " since I'm not a quorum member, I won't receive any information about elected leader")
- message := messageAppend{key: "key100", value: "value100"}
- process.SendAfter(process.Self(), message, 500*time.Millisecond)
- }
- return gen.RaftStatusOK
-}
-
-func (r *Raft) HandleLeader(process *gen.RaftProcess, leader *gen.RaftLeader) gen.RaftStatus {
- if leader != nil && leader.Leader == process.Self() {
- fmt.Println(process.Self(), "I'm a leader of this quorum")
- return gen.RaftStatusOK
- }
-
- if leader != nil {
- fmt.Println(process.Self(), "Leader elected:", leader.Leader, "with serial", leader.Serial)
- }
- s := process.Serial()
- if s < leader.Serial {
- fmt.Println(process.Self(), "Missing serials:", s+1, "..", leader.Serial)
- for i := s + 1; i <= leader.Serial; i++ {
- req, e := process.Get(i)
- if e != nil {
- panic(e)
- }
- fmt.Println(process.Self(), " requested missing serial to the cluster:", i, " id:", req)
- }
- }
- return gen.RaftStatusOK
-}
-
-func (r *Raft) HandleAppend(process *gen.RaftProcess, ref etf.Ref, serial uint64, key string, value etf.Term) gen.RaftStatus {
- fmt.Printf("%s Received append request with serial %d, key %q and value %q\n", process.Self(), serial, key, value)
- r.storage.Append(serial, key, value)
- // check if storage has missing items
- for i := uint64(1); i < serial; i++ {
- _, v := r.storage.Read(i)
- if v != nil {
- continue
- }
- req, e := process.Get(i)
- if e != nil {
- panic(e)
- }
- fmt.Println(process.Self(), "Missing serial:", i, " requested missing serial to the cluster. id", req)
- }
- return gen.RaftStatusOK
-}
-func (r *Raft) HandleGet(process *gen.RaftProcess, serial uint64) (string, etf.Term, gen.RaftStatus) {
- var key string
- var value etf.Term
- fmt.Println(process.Self(), "Received request for serial", serial)
- key, value = r.storage.Read(serial)
- return key, value, gen.RaftStatusOK
-}
-
-func (r *Raft) HandleRaftInfo(process *gen.RaftProcess, message etf.Term) gen.ServerStatus {
- messageAppend, ok := message.(messageAppend)
- if ok == false {
- return gen.ServerStatusOK
- }
- if _, err := process.Append(messageAppend.key, messageAppend.value); err != nil {
- fmt.Println("can't make append request", err)
- return gen.ServerStatusOK
- }
- return gen.ServerStatusOK
-}
-
-func (r *Raft) HandleSerial(process *gen.RaftProcess, ref etf.Ref, serial uint64, key string, value etf.Term) gen.RaftStatus {
- fmt.Println(process.Self(), "Received requested serial", serial, "with key", key, "and value", value)
- r.storage.Append(serial, key, value)
- return gen.RaftStatusOK
-}
diff --git a/examples/genraft/storage.go b/examples/genraft/storage.go
deleted file mode 100644
index a4be9125..00000000
--- a/examples/genraft/storage.go
+++ /dev/null
@@ -1,71 +0,0 @@
-package main
-
-import (
- "github.com/ergo-services/ergo/etf"
-)
-
-var (
- data = map[string]dataValueSerial{
- "key0": dataValueSerial{"value0", 0},
- "key1": dataValueSerial{"value1", 1},
- "key2": dataValueSerial{"value2", 2},
- "key3": dataValueSerial{"value3", 3},
- "key4": dataValueSerial{"value4", 4},
- "key5": dataValueSerial{"value5", 5},
- "key6": dataValueSerial{"value6", 6},
- "key7": dataValueSerial{"value7", 7},
- "key8": dataValueSerial{"value8", 8},
- "key9": dataValueSerial{"value9", 9},
- }
- keySerials = []string{
- "key0",
- "key1",
- "key2",
- "key3",
- "key4",
- "key5",
- "key6",
- "key7",
- "key8",
- "key9",
- }
-)
-
-type storage struct {
- data map[string]dataValueSerial // key/value
- idx [10]string // keys
-}
-
-type dataValueSerial struct {
- value string
- serial uint64
-}
-
-func (s *storage) Init(n int) {
- s.data = make(map[string]dataValueSerial)
- for i := 0; i <= int(n); i++ {
- key := keySerials[i]
- s.data[key] = data[key]
- s.idx[i] = key
- }
-}
-
-func (s *storage) Read(serial uint64) (string, etf.Term) {
- var key string
-
- key = s.idx[int(serial)]
- if key == "" {
- // no data found
- return key, nil
- }
- data := s.data[key]
- return key, data.value
-}
-
-func (s *storage) Append(serial uint64, key string, value etf.Term) {
- s.data[key] = dataValueSerial{
- value: value.(string),
- serial: serial,
- }
- s.idx[serial] = key
-}
diff --git a/examples/gensaga/README.md b/examples/gensaga/README.md
deleted file mode 100644
index f4a4146e..00000000
--- a/examples/gensaga/README.md
+++ /dev/null
@@ -1,73 +0,0 @@
-## Saga demo scenario ##
-
-1. Start Tx on Saga1 with enabled TwoPhaseCommit option. Saga1 sends this Tx to Saga2 with enabled TrapCancel option, Saga2 sends Tx to Saga3.
-
-```
- Saga1 ---> Tx ---> Saga2 ---> Tx ---> Saga3
-```
-
-2. Saga2 terminates, and Saga1 handles it by sending this Tx to Saga4, and Saga4 sends it to Saga3.
-
-```
- --------> Tx ------> Saga4 -------- Tx ------->
- / \
- Saga1 <--- signal DOWN <--- Saga2 ---> signal DOWN ---> Saga3
-
-```
-
-3. Saga1 commits Tx when its done.
-
-```
- --> signal COMMIT --> Saga4 --> signal COMMIT -->
- / \
- Saga1 ............... Saga2 (terminated) ................ Saga3
-
-```
-
-Here is output of this example
-
-```
-❯❯❯❯ go run .
-Starting node: node1@localhost and Saga1 process...OK
-Starting node: node2@localhost and Saga2 process...OK
-Starting node: node3@localhost and Saga3 process...OK
-Starting node: node4@localhost and Saga4 process...OK
-Saga1. Start new transaction TX#25333.23216.0
- Worker started on Saga1 with value "Hello "
-Saga1. Received result from worker with value "Hello W"
-Saga1. ...sent TX#25333.23216.0 further, to the Saga2 on Node2 (Next#25335.23216.0)
-Saga2. Received TX#25333.23216.0 with value "Hello Wo"
- Worker started on Saga2 with value "Hello Wo"
-Saga2. Received result from worker with value "Hello Wor"
-Saga2. ...sent TX#25333.23216.0 further, to the Saga3 on Node3 (Next#18646.23216.0)
-Saga2 termination
- Worker terminated on Saga2 with reason: "normal"
-Saga3. Received TX#25333.23216.0 with value "Hello Worl"
-Saga1. Trapped cancelation TX#25333.23216.0. Reason "next saga {saga2 node2@localhost} is down"
-Saga1. ...sent TX#25333.23216.0 further, to the Saga4 on Node4 (Next#25336.23216.0)
-Saga4. Received TX#25333.23216.0 with value "Hello Wo"
- Worker started on Saga3 with value "Hello Worl"
-Saga3. Received result from worker with value "Hello World"
-Saga3. ...sent result "Hello World!" to the parent saga for TX#25333.23216.0
-Saga3. Canceled TX#25333.23216.0 with reason: "parent saga <59AA5040.0.1008> is down"
- Worker terminated on Saga3 with reason: "normal"
- Worker started on Saga4 with value "Hello Wo"
-Saga4. Received result from worker with value "Hello Wor"
-Saga4. ...sent TX#25333.23216.0 further, to the Saga3 on Node3 (Next#179533.23216.0)
-Saga3. Received TX#25333.23216.0 with value "Hello Worl"
- Worker started on Saga3 with value "Hello Worl"
-Saga3. Received result from worker with value "Hello World"
-Saga3. ...sent result "Hello World!" to the parent saga for TX#25333.23216.0
-Saga4. Received result for TX#25333.23216.0 from Saga3 (Next#179533.23216.0) with value "Hello World!"
-Saga4. ...sent result "Hello World!" to the parent saga for TX#25333.23216.0
-Saga1. Received result for TX#25333.23216.0 from Saga4 (Next#25336.23216.0) with value "Hello World!"
-Saga1. Final result for TX#25333.23216.0: "Hello World! 🚀"
- Worker on Saga1 received final result for TX#25333.23216.0 with value "Hello World! 🚀"
- Worker terminated on Saga1 with reason: "normal"
-Saga4. Final result for TX#25333.23216.0: "Hello World! 🚀"
- Worker on Saga4 received final result for TX#25333.23216.0 with value "Hello World! 🚀"
- Worker terminated on Saga4 with reason: "normal"
-Saga3. Final result for TX#25333.23216.0: "Hello World! 🚀"
- Worker on Saga3 received final result for TX#25333.23216.0 with value "Hello World! 🚀"
- Worker terminated on Saga3 with reason: "normal"
-```
diff --git a/examples/gensaga/main.go b/examples/gensaga/main.go
deleted file mode 100644
index a2ff0f53..00000000
--- a/examples/gensaga/main.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-
- "github.com/ergo-services/ergo"
- "github.com/ergo-services/ergo/gen"
- "github.com/ergo-services/ergo/node"
-)
-
-func main() {
- fmt.Printf("Starting node: node1@localhost and Saga1 process...")
- node1, err := ergo.StartNode("node1@localhost", "cookies", node.Options{})
- if err != nil {
- panic(err)
- }
- saga1 := &Saga1{}
- saga1_process, err := node1.Spawn("saga1", gen.ProcessOptions{MailboxSize: 10000}, saga1)
- if err != nil {
- panic(err)
- }
- fmt.Println("OK")
-
- fmt.Printf("Starting node: node2@localhost and Saga2 process...")
- node2, err := ergo.StartNode("node2@localhost", "cookies", node.Options{})
- if err != nil {
- panic(err)
- }
- saga2 := &Saga2{}
- _, err = node2.Spawn("saga2", gen.ProcessOptions{MailboxSize: 10000}, saga2)
- if err != nil {
- panic(err)
- }
- fmt.Println("OK")
-
- fmt.Printf("Starting node: node3@localhost and Saga3 process...")
- node3, err := ergo.StartNode("node3@localhost", "cookies", node.Options{})
- if err != nil {
- panic(err)
- }
- saga3 := &Saga3{}
- _, err = node3.Spawn("saga3", gen.ProcessOptions{MailboxSize: 10000}, saga3)
- if err != nil {
- panic(err)
- }
- fmt.Println("OK")
-
- fmt.Printf("Starting node: node4@localhost and Saga4 process...")
- node4, err := ergo.StartNode("node4@localhost", "cookies", node.Options{})
- if err != nil {
- panic(err)
- }
- saga4 := &Saga4{}
- _, err = node4.Spawn("saga4", gen.ProcessOptions{MailboxSize: 10000}, saga4)
- if err != nil {
- panic(err)
- }
- fmt.Println("OK")
-
- args := startTX{
- done: make(chan bool),
- }
- saga1_process.Send(saga1_process.Self(), args)
-
- <-args.done
- time.Sleep(300 * time.Millisecond)
-
- node1.Stop()
- node2.Stop()
- node3.Stop()
- node4.Stop()
-}
diff --git a/examples/gensaga/saga1.go b/examples/gensaga/saga1.go
deleted file mode 100644
index ea5b6533..00000000
--- a/examples/gensaga/saga1.go
+++ /dev/null
@@ -1,130 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-type startTX struct {
- done chan bool
-}
-
-//
-// Saga1 worker
-//
-type saga1worker struct {
- gen.SagaWorker
-}
-
-func (w *saga1worker) HandleJobStart(process *gen.SagaWorkerProcess, job gen.SagaJob) error {
- fmt.Printf(" Worker started on Saga1 with value %q\n", job.Value)
- s := job.Value.(string) + "W"
- process.SendResult(s)
- process.State = job.TransactionID
- return nil
-}
-func (w *saga1worker) HandleJobCancel(process *gen.SagaWorkerProcess, reason string) {
- return
-}
-func (w *saga1worker) HandleJobCommit(process *gen.SagaWorkerProcess, final interface{}) {
- txid := process.State.(gen.SagaTransactionID)
- fmt.Printf(" Worker on Saga1 received final result for %v with value %q\n", txid, final)
-}
-
-func (w *saga1worker) HandleWorkerTerminate(process *gen.SagaWorkerProcess, reason string) {
- fmt.Printf(" Worker terminated on Saga1 with reason: %q\n", reason)
-}
-
-//
-// Saga1
-//
-type Saga1 struct {
- gen.Saga
-}
-
-type Saga1State struct {
- done chan bool
- tmp string
-}
-
-func (s *Saga1) InitSaga(process *gen.SagaProcess, args ...etf.Term) (gen.SagaOptions, error) {
- opts := gen.SagaOptions{
- Worker: &saga1worker{},
- }
-
- return opts, nil
-}
-
-func (s *Saga1) HandleTxNew(process *gen.SagaProcess, id gen.SagaTransactionID, value interface{}) gen.SagaStatus {
- // add some sleep
- time.Sleep(300 * time.Millisecond)
- process.StartJob(id, gen.SagaJobOptions{}, value)
- return gen.SagaStatusOK
-}
-
-func (s *Saga1) HandleTxCancel(process *gen.SagaProcess, id gen.SagaTransactionID, reason string) gen.SagaStatus {
- return gen.SagaStatusOK
-}
-
-func (s *Saga1) HandleTxDone(process *gen.SagaProcess, id gen.SagaTransactionID, result interface{}) (interface{}, gen.SagaStatus) {
- final := result.(string) + " 🚀"
- fmt.Printf("Saga1. Final result for %v: %q\n", id, final)
-
- // let the main goroutine know we got finished
- state := process.State.(*Saga1State)
- state.done <- true
-
- return final, gen.SagaStatusOK
-}
-
-func (s *Saga1) HandleTxResult(process *gen.SagaProcess, id gen.SagaTransactionID, from gen.SagaNextID, result interface{}) gen.SagaStatus {
- fmt.Printf("Saga1. Received result for %v from Saga4 (%v) with value %q\n", id, from, result)
- // to finish TX on a saga it was created we must call SendResult with the result
- process.SendResult(id, result)
- return gen.SagaStatusOK
-}
-
-func (s *Saga1) HandleJobResult(process *gen.SagaProcess, id gen.SagaTransactionID, from gen.SagaJobID, result interface{}) gen.SagaStatus {
- // keep result in the process state
- state := process.State.(*Saga1State)
- state.tmp = result.(string)
-
- fmt.Printf("Saga1. Received result from worker with value %q\n", result)
- next := gen.SagaNext{
- Saga: gen.ProcessID{Name: "saga2", Node: "node2@localhost"},
- TrapCancel: true,
- Value: result.(string) + "o",
- }
- next_id, _ := process.Next(id, next)
- fmt.Printf("Saga1. ...sent %v further, to the Saga2 on Node2 (%v)\n", id, next_id)
- return gen.SagaStatusOK
-}
-
-// implement this method in order to trap TX cancelation and forward it to the Saga4
-func (s *Saga1) HandleSagaInfo(process *gen.SagaProcess, message etf.Term) gen.ServerStatus {
- switch m := message.(type) {
- case gen.MessageSagaCancel:
- fmt.Printf("Saga1. Trapped cancelation %v. Reason %q\n", m.TransactionID, m.Reason)
- // process state keeps the value we got from the worker
- state := process.State.(*Saga1State)
- next := gen.SagaNext{
- Saga: gen.ProcessID{Name: "saga4", Node: "node4@localhost"},
- Value: state.tmp + "o",
- }
- next_id, _ := process.Next(m.TransactionID, next)
- fmt.Printf("Saga1. ...sent %v further, to the Saga4 on Node4 (%v)\n", m.TransactionID, next_id)
- case startTX:
- process.State = &Saga1State{
- done: m.done,
- }
- opts := gen.SagaTransactionOptions{
- TwoPhaseCommit: true,
- }
- id := process.StartTransaction(opts, "Hello ")
- fmt.Printf("Saga1. Start new transaction %v\n", id)
- }
- return gen.ServerStatusOK
-}
diff --git a/examples/gensaga/saga2.go b/examples/gensaga/saga2.go
deleted file mode 100644
index 6137600e..00000000
--- a/examples/gensaga/saga2.go
+++ /dev/null
@@ -1,80 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-//
-// Saga2 worker
-//
-type saga2worker struct {
- gen.SagaWorker
-}
-
-func (w *saga2worker) HandleJobStart(process *gen.SagaWorkerProcess, job gen.SagaJob) error {
- fmt.Printf(" Worker started on Saga2 with value %q\n", job.Value)
- s := job.Value.(string) + "r"
- process.SendResult(s)
- process.State = job.TransactionID
- return nil
-}
-func (w *saga2worker) HandleJobCancel(process *gen.SagaWorkerProcess, reason string) {
- return
-}
-func (w *saga2worker) HandleJobCommit(process *gen.SagaWorkerProcess, final interface{}) {
- txid := process.State.(gen.SagaTransactionID)
- fmt.Printf(" Worker on Saga2 received final result for %v with value %q\n", txid, final)
-}
-
-func (w *saga2worker) HandleWorkerTerminate(process *gen.SagaWorkerProcess, reason string) {
- fmt.Printf(" Worker terminated on Saga2 with reason: %q\n", reason)
-}
-
-//
-// Saga2
-//
-type Saga2 struct {
- gen.Saga
-}
-
-func (s *Saga2) InitSaga(process *gen.SagaProcess, args ...etf.Term) (gen.SagaOptions, error) {
- opts := gen.SagaOptions{
- Worker: &saga2worker{},
- }
-
- return opts, nil
-}
-
-func (s *Saga2) HandleTxNew(process *gen.SagaProcess, id gen.SagaTransactionID, value interface{}) gen.SagaStatus {
- // add some sleep
- fmt.Printf("Saga2. Received %v with value %q\n", id, value)
- time.Sleep(300 * time.Millisecond)
- process.StartJob(id, gen.SagaJobOptions{}, value)
- return gen.SagaStatusOK
-}
-
-func (s *Saga2) HandleTxCancel(process *gen.SagaProcess, id gen.SagaTransactionID, reason string) gen.SagaStatus {
- return gen.SagaStatusOK
-}
-
-func (s *Saga2) HandleTxResult(process *gen.SagaProcess, id gen.SagaTransactionID, from gen.SagaNextID, result interface{}) gen.SagaStatus {
- return gen.SagaStatusOK
-}
-
-func (s *Saga2) HandleJobResult(process *gen.SagaProcess, id gen.SagaTransactionID, from gen.SagaJobID, result interface{}) gen.SagaStatus {
-
- fmt.Printf("Saga2. Received result from worker with value %q\n", result)
- next := gen.SagaNext{
- Saga: gen.ProcessID{Name: "saga3", Node: "node3@localhost"},
- Value: result.(string) + "l",
- }
- next_id, _ := process.Next(id, next)
- fmt.Printf("Saga2. ...sent %v further, to the Saga3 on Node3 (%v)\n", id, next_id)
-
- fmt.Println("Saga2 termination")
- return gen.SagaStatusStop
-}
diff --git a/examples/gensaga/saga3.go b/examples/gensaga/saga3.go
deleted file mode 100644
index 4736c09e..00000000
--- a/examples/gensaga/saga3.go
+++ /dev/null
@@ -1,85 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-//
-// Saga3 worker
-//
-type saga3worker struct {
- gen.SagaWorker
-}
-
-func (w *saga3worker) HandleJobStart(process *gen.SagaWorkerProcess, job gen.SagaJob) error {
- fmt.Printf(" Worker started on Saga3 with value %q\n", job.Value)
- s := job.Value.(string) + "d"
- process.SendResult(s)
- process.State = job.TransactionID
- return nil
-}
-func (w *saga3worker) HandleJobCancel(process *gen.SagaWorkerProcess, reason string) {
- return
-}
-func (w *saga3worker) HandleJobCommit(process *gen.SagaWorkerProcess, final interface{}) {
- txid := process.State.(gen.SagaTransactionID)
- fmt.Printf(" Worker on Saga3 received final result for %v with value %q\n", txid, final)
-}
-
-func (w *saga3worker) HandleWorkerTerminate(process *gen.SagaWorkerProcess, reason string) {
- fmt.Printf(" Worker terminated on Saga3 with reason: %q\n", reason)
-}
-
-//
-// Saga3
-//
-type Saga3 struct {
- gen.Saga
-}
-
-func (s *Saga3) InitSaga(process *gen.SagaProcess, args ...etf.Term) (gen.SagaOptions, error) {
- opts := gen.SagaOptions{
- Worker: &saga3worker{},
- }
-
- return opts, nil
-}
-
-func (s *Saga3) HandleTxNew(process *gen.SagaProcess, id gen.SagaTransactionID, value interface{}) gen.SagaStatus {
- // add some sleep
- fmt.Printf("Saga3. Received %v with value %q\n", id, value)
- time.Sleep(300 * time.Millisecond)
- process.StartJob(id, gen.SagaJobOptions{}, value)
- return gen.SagaStatusOK
-}
-
-func (s *Saga3) HandleTxCancel(process *gen.SagaProcess, id gen.SagaTransactionID, reason string) gen.SagaStatus {
- fmt.Printf("Saga3. Canceled %v with reason: %q\n", id, reason)
- return gen.SagaStatusOK
-}
-
-func (s *Saga3) HandleTxResult(process *gen.SagaProcess, id gen.SagaTransactionID, from gen.SagaNextID, result interface{}) gen.SagaStatus {
- return gen.SagaStatusOK
-}
-
-func (s *Saga3) HandleJobResult(process *gen.SagaProcess, id gen.SagaTransactionID, from gen.SagaJobID, result interface{}) gen.SagaStatus {
-
- fmt.Printf("Saga3. Received result from worker with value %q\n", result)
- r := result.(string) + "!"
- if err := process.SendResult(id, r); err != nil {
- fmt.Printf("Saga3. ...can't send result %q to the parent saga for %v due to: %s\n", r, id, err)
-
- } else {
- fmt.Printf("Saga3. ...sent result %q to the parent saga for %v\n", r, id)
- }
- return gen.SagaStatusOK
-}
-
-func (s *Saga3) HandleTxCommit(process *gen.SagaProcess, id gen.SagaTransactionID, final interface{}) gen.SagaStatus {
- fmt.Printf("Saga3. Final result for %v: %q\n", id, final)
- return gen.SagaStatusOK
-}
diff --git a/examples/gensaga/saga4.go b/examples/gensaga/saga4.go
deleted file mode 100644
index 21ea11d7..00000000
--- a/examples/gensaga/saga4.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-//
-// Saga4 worker
-//
-type saga4worker struct {
- gen.SagaWorker
-}
-
-func (w *saga4worker) HandleJobStart(process *gen.SagaWorkerProcess, job gen.SagaJob) error {
- fmt.Printf(" Worker started on Saga4 with value %q\n", job.Value)
- s := job.Value.(string) + "r"
- process.SendResult(s)
- process.State = job.TransactionID
- return nil
-}
-func (w *saga4worker) HandleJobCancel(process *gen.SagaWorkerProcess, reason string) {
- return
-}
-func (w *saga4worker) HandleJobCommit(process *gen.SagaWorkerProcess, final interface{}) {
- txid := process.State.(gen.SagaTransactionID)
- fmt.Printf(" Worker on Saga4 received final result for %v with value %q\n", txid, final)
-}
-
-func (w *saga4worker) HandleWorkerTerminate(process *gen.SagaWorkerProcess, reason string) {
- fmt.Printf(" Worker terminated on Saga4 with reason: %q\n", reason)
-}
-
-//
-// Saga4 (the same as Saga2, but with no termination)
-//
-type Saga4 struct {
- gen.Saga
-}
-
-func (s *Saga4) InitSaga(process *gen.SagaProcess, args ...etf.Term) (gen.SagaOptions, error) {
- opts := gen.SagaOptions{
- Worker: &saga4worker{},
- }
-
- return opts, nil
-}
-
-func (s *Saga4) HandleTxNew(process *gen.SagaProcess, id gen.SagaTransactionID, value interface{}) gen.SagaStatus {
- // add some sleep
- fmt.Printf("Saga4. Received %v with value %q\n", id, value)
- time.Sleep(300 * time.Millisecond)
- process.StartJob(id, gen.SagaJobOptions{}, value)
- return gen.SagaStatusOK
-}
-
-func (s *Saga4) HandleTxCancel(process *gen.SagaProcess, id gen.SagaTransactionID, reason string) gen.SagaStatus {
- return gen.SagaStatusOK
-}
-
-func (s *Saga4) HandleTxResult(process *gen.SagaProcess, id gen.SagaTransactionID, from gen.SagaNextID, result interface{}) gen.SagaStatus {
- fmt.Printf("Saga4. Received result for %v from Saga3 (%v) with value %q\n", id, from, result)
- process.SendResult(id, result)
- fmt.Printf("Saga4. ...sent result %q to the parent saga for %v\n", result, id)
- return gen.SagaStatusOK
-}
-
-func (s *Saga4) HandleJobResult(process *gen.SagaProcess, id gen.SagaTransactionID, from gen.SagaJobID, result interface{}) gen.SagaStatus {
-
- fmt.Printf("Saga4. Received result from worker with value %q\n", result)
- next := gen.SagaNext{
- Saga: gen.ProcessID{Name: "saga3", Node: "node3@localhost"},
- Value: result.(string) + "l",
- }
- next_id, _ := process.Next(id, next)
- fmt.Printf("Saga4. ...sent %v further, to the Saga3 on Node3 (%v)\n", id, next_id)
- return gen.SagaStatusOK
-}
-
-func (s *Saga4) HandleTxCommit(process *gen.SagaProcess, id gen.SagaTransactionID, final interface{}) gen.SagaStatus {
- fmt.Printf("Saga4. Final result for %v: %q\n", id, final)
- return gen.SagaStatusOK
-}
diff --git a/examples/genserver/README.md b/examples/genserver/README.md
deleted file mode 100644
index c1deccb5..00000000
--- a/examples/genserver/README.md
+++ /dev/null
@@ -1,25 +0,0 @@
-## Server demo scenario ##
-
-This example implements simple server which sends a message to itself.
-
-Here is output of this example:
-
-```
-❯❯❯❯ go run .
-Start node mynode@localhost
-Started process <0ABD9258.0.1011> with name "simple"
-Send message 100 to itself
-HandleInfo: 100
-increase this value by 1 and send it to itself again
-HandleInfo: 101
-increase this value by 1 and send it to itself again
-HandleInfo: 102
-increase this value by 1 and send it to itself again
-HandleInfo: 103
-increase this value by 1 and send it to itself again
-HandleInfo: 104
-increase this value by 1 and send it to itself again
-HandleInfo: 105
-Stop node mynode@localhost
-
-```
diff --git a/examples/genserver/main.go b/examples/genserver/main.go
deleted file mode 100644
index 06187a82..00000000
--- a/examples/genserver/main.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
-
- "github.com/ergo-services/ergo"
- "github.com/ergo-services/ergo/gen"
- "github.com/ergo-services/ergo/node"
-)
-
-func main() {
- flag.Parse()
-
- fmt.Println("Start node mynode@localhost")
- myNode, _ := ergo.StartNode("mynode@localhost", "cookies", node.Options{})
-
- process, _ := myNode.Spawn("simple", gen.ProcessOptions{}, &simple{})
- fmt.Printf("Started process %s with name %q\n", process.Self(), process.Name())
-
- // send a message to itself
- fmt.Println("Send message 100 to itself")
- process.Send(process.Self(), 100)
-
- // wait for the prokcess termination.
- process.Wait()
- fmt.Println("Stop node", myNode.Name())
- myNode.Stop()
-}
diff --git a/examples/genserver/server.go b/examples/genserver/server.go
deleted file mode 100644
index 24a5530e..00000000
--- a/examples/genserver/server.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package main
-
-import (
- "fmt"
- "time"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-// simple implementation of Server
-type simple struct {
- gen.Server
-}
-
-func (s *simple) HandleInfo(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
- value := message.(int)
- fmt.Printf("HandleInfo: %#v \n", message)
- if value > 104 {
- return gen.ServerStatusStop
- }
- // sending message with delay 1 second
- fmt.Println("increase this value by 1 and send it to itself again")
- process.SendAfter(process.Self(), value+1, time.Second)
- return gen.ServerStatusOK
-}
diff --git a/examples/genstage/README.md b/examples/genstage/README.md
deleted file mode 100644
index 51decf9d..00000000
--- a/examples/genstage/README.md
+++ /dev/null
@@ -1,37 +0,0 @@
-## Stage demo scenario ##
-
-This example demonstrates a simple implementation of publisher/subscriber using gen.Stage behavior.
-The basic scenario is sending numbers to the consumers but distinguishing them on odd and even.
-
-It starts with two nodes - `node_abc@localhost` with one producer and `node_def@localhost` with two consumers. In this example, the producer uses a `partition` dispatcher with the hash function to distinguish numbers (there are three types of dispatchers - `demand`, `broadcast`, `partition`).
-
-Here is output of this example
-
-```
-❯❯❯❯ go run .
-
-to stop press Ctrl-C
-
-Starting nodes 'node_abc@localhost' and 'node_def@localhost'
-Spawn producer on 'node_abc@localhost'
-Spawn 2 consumers on 'node_def@localhost'
-Subscribe consumer even [ <6E7F6C08.0.1011> ] with min events = 1 and max events 2
-Subscribe consumer odd [ <6E7F6C08.0.1012> ] with min events = 2 and max events 4
-New subscription from: <6E7F6C08.0.1011> with min: 1 and max: 2
-Producer: just got demand for 2 event(s) from <6E7F6C08.0.1011>
-Producer. Generate random numbers and send them to consumers... [62 75 36 10 56]
-New subscription from: <6E7F6C08.0.1012> with min: 2 and max: 4
-Consumer 'even' got events: [62 36]
-Producer: just got demand for 2 event(s) from <6E7F6C08.0.1011>
-Producer. Generate random numbers and send them to consumers... [74 64 57 60 6]
-Consumer 'even' got events: [60 6]
-Consumer 'even' got events: [10 56]
-Consumer 'even' got events: [74 64]
-Producer: just got demand for 2 event(s) from <6E7F6C08.0.1011>
-Producer. Generate random numbers and send them to consumers... [58 62 60 53 63]
-Consumer 'even' got events: [60]
-Consumer 'even' got events: [58 62]
-Producer: just got demand for 2 event(s) from <6E7F6C08.0.1011>
-Producer. Generate random numbers and send them to consumers... [47 58 25 93 92]
-Consumer 'even' got events: [58 92]
-```
diff --git a/examples/genstage/consumer.go b/examples/genstage/consumer.go
deleted file mode 100644
index 58c4e805..00000000
--- a/examples/genstage/consumer.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-type Consumer struct {
- gen.Stage
-}
-
-func (c *Consumer) InitStage(process *gen.StageProcess, args ...etf.Term) (gen.StageOptions, error) {
- var opts gen.StageSubscribeOptions
- even := args[0].(bool)
- if even {
- opts = gen.StageSubscribeOptions{
- MinDemand: 1,
- MaxDemand: 2,
- Partition: 0,
- }
- } else {
- opts = gen.StageSubscribeOptions{
- MinDemand: 2,
- MaxDemand: 4,
- Partition: 1,
- }
- }
- fmt.Println("Subscribe consumer", process.Name(), "[", process.Self(), "]",
- "with min events =", opts.MinDemand,
- "and max events", opts.MaxDemand)
- process.Subscribe(gen.ProcessID{Name: "producer", Node: "node_abc@localhost"}, opts)
- return gen.StageOptions{}, nil
-}
-func (c *Consumer) HandleEvents(process *gen.StageProcess, subscription gen.StageSubscription, events etf.List) gen.StageStatus {
- fmt.Printf("Consumer '%s' got events: %v\n", process.Name(), events)
- return gen.StageStatusOK
-}
diff --git a/examples/genstage/main.go b/examples/genstage/main.go
deleted file mode 100644
index 2c2533c3..00000000
--- a/examples/genstage/main.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
-
- "github.com/ergo-services/ergo"
- "github.com/ergo-services/ergo/gen"
- "github.com/ergo-services/ergo/node"
-)
-
-func main() {
- flag.Parse()
-
- fmt.Println("")
- fmt.Println("to stop press Ctrl-C")
- fmt.Println("")
-
- // create nodes for producer and consumers
- fmt.Println("Starting nodes 'node_abc@localhost' and 'node_def@localhost'")
- node_abc, _ := ergo.StartNode("node_abc@localhost", "cookies", node.Options{})
- node_def, _ := ergo.StartNode("node_def@localhost", "cookies", node.Options{})
-
- // create producer and consumer objects
- producer := &Producer{}
- consumer := &Consumer{}
-
- fmt.Println("Spawn producer on 'node_abc@localhost'")
- _, errP := node_abc.Spawn("producer", gen.ProcessOptions{}, producer, nil)
- if errP != nil {
- panic(errP)
- }
- fmt.Println("Spawn 2 consumers on 'node_def@localhost'")
- _, errC1 := node_def.Spawn("even", gen.ProcessOptions{}, consumer, true)
- if errC1 != nil {
- panic(errC1)
- }
- _, errC2 := node_def.Spawn("odd", gen.ProcessOptions{}, consumer, false)
- if errC2 != nil {
- panic(errC2)
- }
-
- node_abc.Wait()
-
-}
diff --git a/examples/genstage/producer.go b/examples/genstage/producer.go
deleted file mode 100644
index 59b11b16..00000000
--- a/examples/genstage/producer.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package main
-
-import (
- "fmt"
- "math/rand"
- "time"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-type Producer struct {
- gen.Stage
- dispatcher gen.StageDispatcherBehavior
-}
-
-func (p *Producer) InitStage(process *gen.StageProcess, args ...etf.Term) (gen.StageOptions, error) {
- // create a hash function for the dispatcher
- hash := func(t etf.Term) int {
- i, ok := t.(int)
- if !ok {
- // filtering out
- return -1
- }
- if i%2 == 0 {
- return 0
- }
- return 1
- }
-
- options := gen.StageOptions{
- Dispatcher: gen.CreateStageDispatcherPartition(3, hash),
- }
- return options, nil
-}
-func (p *Producer) HandleDemand(process *gen.StageProcess, subscription gen.StageSubscription, count uint) (etf.List, gen.StageStatus) {
- fmt.Println("Producer: just got demand for", count, "event(s) from", subscription.Pid)
- numbers := generateNumbers(int(count) + 3)
- fmt.Println("Producer. Generate random numbers and send them to consumers...", numbers)
- process.SendEvents(numbers)
- time.Sleep(500 * time.Millisecond)
- return nil, gen.StageStatusOK
-}
-
-func (p *Producer) HandleSubscribe(process *gen.StageProcess, subscription gen.StageSubscription, options gen.StageSubscribeOptions) gen.StageStatus {
- fmt.Println("New subscription from:", subscription.Pid, "with min:", options.MinDemand, "and max:", options.MaxDemand)
- return gen.StageStatusOK
-}
-
-func generateNumbers(n int) etf.List {
- l := etf.List{}
- for n > 0 {
- l = append(l, rand.Intn(100))
- n--
- }
- return l
-}
diff --git a/examples/gentcp/README.md b/examples/gentcp/README.md
deleted file mode 100644
index 8a17fdee..00000000
--- a/examples/gentcp/README.md
+++ /dev/null
@@ -1,24 +0,0 @@
-## TCP demo scenario ##
-
-This example implements a simple application that starts the child process with TCP server
-
-Here is output of this example:
-```
-❯❯❯❯ go run . -tls
-Start node tcp@127.0.0.1
-TLS enabled. Generated self signed certificate. You may check it with command below:
- $ openssl s_client -connect :8383
-Application started!
-[TCP handler] got new connection from "127.0.0.1:35962"
-send string "d1f5d8f197f0a4c8" to "127.0.0.1:8383"
-[TCP handler] got message from "127.0.0.1:35962": "d1f5d8f197f0a4c8"
-send string "4abff0c11d36ed56" to "127.0.0.1:8383"
-[TCP handler] got message from "127.0.0.1:35962": "4abff0c11d36ed56"
-send string "d2c5e85c04c0b946" to "127.0.0.1:8383"
-[TCP handler] got message from "127.0.0.1:35962": "d2c5e85c04c0b946"
-send string "782559e4d6ec170c" to "127.0.0.1:8383"
-[TCP handler] got message from "127.0.0.1:35962": "782559e4d6ec170c"
-send string "08d2eced5329143c" to "127.0.0.1:8383"
-[TCP handler] got message from "127.0.0.1:35962": "08d2eced5329143c"
-stop node tcp@127.0.0.1
-```
diff --git a/examples/gentcp/app.go b/examples/gentcp/app.go
deleted file mode 100644
index c7a6b154..00000000
--- a/examples/gentcp/app.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-type tcpApp struct {
- gen.Application
-}
-
-func (ta *tcpApp) Load(args ...etf.Term) (gen.ApplicationSpec, error) {
- return gen.ApplicationSpec{
- Name: "tcpApp",
- Description: "Demo TCP Applicatoin",
- Version: "v.1.0",
- Children: []gen.ApplicationChildSpec{
- gen.ApplicationChildSpec{
- Child: &tcpServer{}, // tcp_server.go
- Name: "tcp",
- },
- },
- }, nil
-}
-
-func (ta *tcpApp) Start(process gen.Process, args ...etf.Term) {
- fmt.Println("Application started!")
-}
diff --git a/examples/gentcp/main.go b/examples/gentcp/main.go
deleted file mode 100644
index 7751dde7..00000000
--- a/examples/gentcp/main.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package main
-
-import (
- "crypto/tls"
- "flag"
- "fmt"
- "net"
- "strconv"
- "time"
-
- "github.com/ergo-services/ergo"
- "github.com/ergo-services/ergo/gen"
- "github.com/ergo-services/ergo/lib"
- "github.com/ergo-services/ergo/node"
-)
-
-var (
- TCPListenPort int
- TCPListenHost string
- TCPEnableTLS bool
-)
-
-func init() {
- flag.IntVar(&TCPListenPort, "port", 8383, "listen port number")
- flag.StringVar(&TCPListenHost, "host", "", "listen on host")
- flag.BoolVar(&TCPEnableTLS, "tls", false, "enable TLS")
-}
-
-func main() {
- var connection net.Conn
- var err error
-
- flag.Parse()
- opts := node.Options{
- Applications: []gen.ApplicationBehavior{
- &tcpApp{}, // app.go
- },
- }
-
- fmt.Println("Start node", "tcp@127.0.0.1")
- tcpNode, err := ergo.StartNode("tcp@127.0.0.1", "secret", opts)
- if err != nil {
- panic(err)
- }
-
- hostPort := net.JoinHostPort(TCPListenHost, strconv.Itoa(TCPListenPort))
- dialer := net.Dialer{}
-
- if TCPEnableTLS {
- tlsdialer := tls.Dialer{
- NetDialer: &dialer,
- Config: &tls.Config{
- InsecureSkipVerify: true,
- },
- }
- connection, err = tlsdialer.Dial("tcp", hostPort)
- } else {
- connection, err = dialer.Dial("tcp", hostPort)
- }
-
- if err != nil {
- return
- }
-
- defer connection.Close()
-
- for i := 0; i < 5; i++ {
- str := lib.RandomString(16)
-
- fmt.Printf("send string %q to %q\n", str, connection.RemoteAddr().String())
- connection.Write([]byte(str))
- time.Sleep(time.Second)
- }
-
- fmt.Println("stop node", tcpNode.Name())
- tcpNode.Stop()
-}
diff --git a/examples/gentcp/tcp_handler.go b/examples/gentcp/tcp_handler.go
deleted file mode 100644
index 936f3cdf..00000000
--- a/examples/gentcp/tcp_handler.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/ergo-services/ergo/gen"
-)
-
-type tcpHandler struct {
- gen.TCPHandler
-}
-
-func (th *tcpHandler) HandleConnect(process *gen.TCPHandlerProcess, conn *gen.TCPConnection) gen.TCPHandlerStatus {
- fmt.Printf("[TCP handler] got new connection from %q\n", conn.Addr.String())
- return gen.TCPHandlerStatusOK
-}
-func (th *tcpHandler) HandleDisconnect(process *gen.TCPHandlerProcess, conn *gen.TCPConnection) {
- fmt.Printf("[TCP handler] connection with %q terminated\n", conn.Addr.String())
-}
-
-func (th *tcpHandler) HandlePacket(process *gen.TCPHandlerProcess, packet []byte, conn *gen.TCPConnection) (int, int, gen.TCPHandlerStatus) {
- fmt.Printf("[TCP handler] got message from %q: %q\n", conn.Addr.String(), string(packet))
-
- // If you want to send a reply message, use conn.Socket.Write(reply) for that.
-
- // You may keep any data related to this connection in conn.State
-
- // return values: left, await, status
- // left - how many bytes are left in the packet buffer (you might have
- // received a part of the next logical data).
- // await - what exact number of bytes you expect in the next packet
- // or leave it 0 if you are unsure.
- // status - return gen.TCPHandlerStatusClose to close this connection
-
- // example:
- // expected data of 5 bytes, but have received packet = []byte{1,2,3,4,5,6,7,8}
- // you must return 3, 5, gen.TCPHandlerStatusOK
- // So the following invocation will happen after receiving two more bytes
- // The next packet will have []byte{6,7,8,9,0}
- return 0, 0, gen.TCPHandlerStatusOK
-}
diff --git a/examples/gentcp/tcp_server.go b/examples/gentcp/tcp_server.go
deleted file mode 100644
index 5f9d14cb..00000000
--- a/examples/gentcp/tcp_server.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package main
-
-import (
- "crypto/tls"
- "fmt"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
- "github.com/ergo-services/ergo/lib"
-)
-
-type tcpServer struct {
- gen.TCP
-}
-
-func (ts *tcpServer) InitTCP(process *gen.TCPProcess, args ...etf.Term) (gen.TCPOptions, error) {
- options := gen.TCPOptions{
- Host: TCPListenHost,
- Port: uint16(TCPListenPort),
- Handler: &tcpHandler{},
- }
-
- if TCPEnableTLS {
- cert, _ := lib.GenerateSelfSignedCert("localhost")
- fmt.Println("TLS enabled. Generated self signed certificate. You may check it with command below:")
- fmt.Printf(" $ openssl s_client -connect %s:%d\n", TCPListenHost, TCPListenPort)
- options.TLS = &tls.Config{
- Certificates: []tls.Certificate{cert},
- InsecureSkipVerify: true,
- }
- }
-
- return options, nil
-}
diff --git a/examples/genudp/README.md b/examples/genudp/README.md
deleted file mode 100644
index 9573b72c..00000000
--- a/examples/genudp/README.md
+++ /dev/null
@@ -1,21 +0,0 @@
-## UDP demo scenario ##
-
-This example implements a simple application that starts the child process with UDP server
-
-Here is output of this example:
-```
-❯❯❯❯ go run .
-Start node udp@127.0.0.1
-Application started!
-send string "b65574c95fbdcd8e" to "[::1]:5533"
-[UDP handler] got message from "[::1]:49634": "b65574c95fbdcd8e"
-send string "64c203bbf65e121c" to "[::1]:5533"
-[UDP handler] got message from "[::1]:49634": "64c203bbf65e121c"
-send string "2f2d280a8ad76bd4" to "[::1]:5533"
-[UDP handler] got message from "[::1]:49634": "2f2d280a8ad76bd4"
-send string "7c125d26809ec335" to "[::1]:5533"
-[UDP handler] got message from "[::1]:49634": "7c125d26809ec335"
-send string "01317dae8aa231b2" to "[::1]:5533"
-[UDP handler] got message from "[::1]:49634": "01317dae8aa231b2"
-stop node udp@127.0.0.1
-```
diff --git a/examples/genudp/app.go b/examples/genudp/app.go
deleted file mode 100644
index 827e66c3..00000000
--- a/examples/genudp/app.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-type udpApp struct {
- gen.Application
-}
-
-func (ua *udpApp) Load(args ...etf.Term) (gen.ApplicationSpec, error) {
- return gen.ApplicationSpec{
- Name: "udpApp",
- Description: "Demo UDP Applicatoin",
- Version: "v.1.0",
- Children: []gen.ApplicationChildSpec{
- gen.ApplicationChildSpec{
- Child: &udpServer{}, // udp_server.go
- Name: "udp",
- },
- },
- }, nil
-}
-
-func (ua *udpApp) Start(process gen.Process, args ...etf.Term) {
- fmt.Println("Application started!")
-}
diff --git a/examples/genudp/main.go b/examples/genudp/main.go
deleted file mode 100644
index f4023dfb..00000000
--- a/examples/genudp/main.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
- "net"
- "strconv"
- "time"
-
- "github.com/ergo-services/ergo"
- "github.com/ergo-services/ergo/gen"
- "github.com/ergo-services/ergo/lib"
- "github.com/ergo-services/ergo/node"
-)
-
-var (
- UDPListenPort int
- UDPListenHost string
-)
-
-func init() {
- flag.IntVar(&UDPListenPort, "port", 5533, "listen port number")
- flag.StringVar(&UDPListenHost, "host", "localhost", "listen on host")
-}
-
-func main() {
- flag.Parse()
-
- opts := node.Options{
- Applications: []gen.ApplicationBehavior{
- &udpApp{}, // app.go
- },
- }
-
- fmt.Println("Start node", "udp@127.0.0.1")
- udpNode, err := ergo.StartNode("udp@127.0.0.1", "secret", opts)
- if err != nil {
- panic(err)
- }
-
- hostPort := net.JoinHostPort(UDPListenHost, strconv.Itoa(UDPListenPort))
- c, err := net.Dial("udp", hostPort)
- if err != nil {
- return
- }
- defer c.Close()
- for i := 0; i < 5; i++ {
- str := lib.RandomString(16)
-
- fmt.Printf("send string %q to %q\n", str, c.RemoteAddr().String())
- c.Write([]byte(str))
- time.Sleep(time.Second)
- }
-
- fmt.Println("stop node", udpNode.Name())
- udpNode.Stop()
-}
diff --git a/examples/genudp/udp_handler.go b/examples/genudp/udp_handler.go
deleted file mode 100644
index a9b228c4..00000000
--- a/examples/genudp/udp_handler.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/ergo-services/ergo/gen"
-)
-
-type udpHandler struct {
- gen.UDPHandler
-}
-
-func (uh *udpHandler) HandlePacket(process *gen.UDPHandlerProcess, data []byte, packet gen.UDPPacket) {
- fmt.Printf("[UDP handler] got message from %q: %q\n", packet.Addr.String(), string(data))
-
- // If you want to send a reply message, use packet.Socket.Write(reply) for that.
-}
diff --git a/examples/genudp/udp_server.go b/examples/genudp/udp_server.go
deleted file mode 100644
index 040ada26..00000000
--- a/examples/genudp/udp_server.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package main
-
-import (
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-type udpServer struct {
- gen.UDP
-}
-
-func (us *udpServer) InitUDP(process *gen.UDPProcess, args ...etf.Term) (gen.UDPOptions, error) {
- return gen.UDPOptions{
- Host: UDPListenHost,
- Port: uint16(UDPListenPort),
- Handler: &udpHandler{}, // udp_handler.go
- }, nil
-}
diff --git a/examples/genweb/README.md b/examples/genweb/README.md
deleted file mode 100644
index a79125b1..00000000
--- a/examples/genweb/README.md
+++ /dev/null
@@ -1,39 +0,0 @@
-## Web demo scenario ##
-
-This example implements a simple application that starts two child processes - webServer and timeServer.
-
-```
-webNode (node.Node) main.go -> webApp (gen.Application) app.go
- |
- -> webServer (gen.Web) web.go
- | |
- | ->> url '/' handler (gen.WebHandler) web_root_handler.go
- | ->> url '/time/' handler (gen.WebHandler) web_time_handler.go
- |
- -> timeServer (gen.Server) time.go
-```
-
-`webServer` implements gen.Web behavior and defines options for the HTTP server and HTTP handlers with two endpoints:
- * `/` - simple response with "Hello"
- * `/time/` - demonstrates async handling HTTP requests using `timeServer`
-
-By default, it starts HTTP server on port 8080 so you can check it using your web-browser [http://localhost:8080/](http://localhost:8080/) or [http://localhost:8080/time/](http://localhost:8080/time/).
-
-You may also want to benchmark this example on your hardware using the popular tool [wrk](https://github.com/wg/wrk). Here is the result of benchmarking on the AMD Ryzen Threadripper 3970X (64) @ 3.700GHz:
-
-```
-❯❯❯❯ wrk -t32 -c5000 --latency http://localhost:8080/
-Running 10s test @ http://localhost:8080/
- 32 threads and 5000 connections
- Thread Stats Avg Stdev Max +/- Stdev
- Latency 14.44ms 20.62ms 389.13ms 88.66%
- Req/Sec 19.29k 2.85k 69.93k 84.94%
- Latency Distribution
- 50% 6.93ms
- 75% 19.59ms
- 90% 37.81ms
- 99% 98.17ms
- 6149762 requests in 10.10s, 709.65MB read
-Requests/sec: 608973.73
-Transfer/sec: 70.27MB
-```
diff --git a/examples/genweb/app.go b/examples/genweb/app.go
deleted file mode 100644
index a82a1ba2..00000000
--- a/examples/genweb/app.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-type webApp struct {
- gen.Application
-}
-
-func (wa *webApp) Load(args ...etf.Term) (gen.ApplicationSpec, error) {
- return gen.ApplicationSpec{
- Name: "webApp",
- Description: "Demo Web Applicatoin",
- Version: "v.1.0",
- Children: []gen.ApplicationChildSpec{
- gen.ApplicationChildSpec{
- Child: &webServer{}, // web.go
- Name: "web",
- },
- gen.ApplicationChildSpec{
- Child: &timeServer{}, // time.go
- Name: "time",
- },
- },
- }, nil
-}
-
-func (wa *webApp) Start(process gen.Process, args ...etf.Term) {
- fmt.Println("Application started!")
-}
diff --git a/examples/genweb/main.go b/examples/genweb/main.go
deleted file mode 100644
index e758177b..00000000
--- a/examples/genweb/main.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
-
- "github.com/ergo-services/ergo"
- "github.com/ergo-services/ergo/gen"
- "github.com/ergo-services/ergo/node"
-)
-
-var (
- WebListenPort int
- WebListenHost string
- WebEnableTLS bool
-)
-
-func init() {
- flag.IntVar(&WebListenPort, "port", 8080, "listen port number. Default port 8080, for TLS 8443")
- flag.StringVar(&WebListenHost, "host", "localhost", "listen on host")
- flag.BoolVar(&WebEnableTLS, "tls", false, "enable TLS")
-}
-
-func main() {
- fmt.Println("")
- fmt.Println("to stop press Ctrl-C")
- fmt.Println("")
-
- flag.Parse()
-
- opts := node.Options{
- Applications: []gen.ApplicationBehavior{
- &webApp{}, // app.go
- },
- }
-
- webNode, err := ergo.StartNode("web@127.0.0.1", "secret", opts)
- if err != nil {
- panic(err)
- }
-
- webNode.Wait()
-}
diff --git a/examples/genweb/time.go b/examples/genweb/time.go
deleted file mode 100644
index 8b7a32c5..00000000
--- a/examples/genweb/time.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package main
-
-import (
- "time"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
- "github.com/ergo-services/ergo/lib"
-)
-
-type timeServer struct {
- gen.Server
-}
-
-type messageTimeServerRequest struct {
- from etf.Pid
- ref etf.Ref
-}
-
-type messageTimeServerReply struct {
- ref etf.Ref
- time time.Time
-}
-
-func (ts *timeServer) HandleCast(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
- switch m := message.(type) {
- case messageTimeServerRequest:
- reply := messageTimeServerReply{
- ref: m.ref,
- time: time.Now(),
- }
- process.Cast(m.from, reply)
- default:
- lib.Warning("got unknown message %#v", m)
- }
- return gen.ServerStatusOK
-}
diff --git a/examples/genweb/web.go b/examples/genweb/web.go
deleted file mode 100644
index 3e7d52d6..00000000
--- a/examples/genweb/web.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package main
-
-import (
- "crypto/tls"
- "fmt"
- "net/http"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
- "github.com/ergo-services/ergo/lib"
-)
-
-type webServer struct {
- gen.Web
-}
-
-func (w *webServer) InitWeb(process *gen.WebProcess, args ...etf.Term) (gen.WebOptions, error) {
- var options gen.WebOptions
-
- options.Port = uint16(WebListenPort)
- options.Host = WebListenHost
- proto := "http"
- if WebEnableTLS {
- cert, err := lib.GenerateSelfSignedCert("gen.Web demo")
- if err != nil {
- return options, err
- }
- options.TLS = &tls.Config{
- Certificates: []tls.Certificate{cert},
- }
- proto = "https"
- }
-
- mux := http.NewServeMux()
- whOptions := gen.WebHandlerOptions{
- NumHandlers: 50,
- IdleTimeout: 10,
- RequestTimeout: 20,
- }
- webRoot := process.StartWebHandler(&rootHandler{}, whOptions)
- webTime := process.StartWebHandler(&timeHandler{}, gen.WebHandlerOptions{})
- mux.Handle("/", webRoot)
- mux.Handle("/time/", webTime)
- options.Handler = mux
-
- fmt.Printf("Start Web server on %s://%s:%d/\n", proto, WebListenHost, WebListenPort)
-
- return options, nil
-}
diff --git a/examples/genweb/web_root_handler.go b/examples/genweb/web_root_handler.go
deleted file mode 100644
index 8734cd40..00000000
--- a/examples/genweb/web_root_handler.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package main
-
-import (
- "github.com/ergo-services/ergo/gen"
-)
-
-type rootHandler struct {
- gen.WebHandler
-}
-
-func (r *rootHandler) HandleRequest(process *gen.WebHandlerProcess, request gen.WebMessageRequest) gen.WebHandlerStatus {
- request.Response.Write([]byte("Hello"))
- return gen.WebHandlerStatusDone
-}
diff --git a/examples/genweb/web_time_handler.go b/examples/genweb/web_time_handler.go
deleted file mode 100644
index ecd70e45..00000000
--- a/examples/genweb/web_time_handler.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package main
-
-import (
- "fmt"
- "net/http"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-type timeHandler struct {
- gen.WebHandler
-}
-
-type processState struct {
- asyncRequests map[etf.Ref]gen.WebMessageRequest
-}
-
-func (th *timeHandler) HandleRequest(process *gen.WebHandlerProcess, request gen.WebMessageRequest) gen.WebHandlerStatus {
- state, ok := process.State.(*processState)
- if !ok {
- state = &processState{
- asyncRequests: make(map[etf.Ref]gen.WebMessageRequest),
- }
- process.State = state
- }
- mt := messageTimeServerRequest{
- ref: request.Ref,
- from: process.Self(),
- }
- if err := process.Cast("time", mt); err == nil {
- state.asyncRequests[mt.ref] = request
- return gen.WebHandlerStatusWait
- }
-
- request.Response.WriteHeader(http.StatusServiceUnavailable) // 503
- return gen.WebHandlerStatusDone
-}
-
-func (th *timeHandler) HandleWebHandlerCast(process *gen.WebHandlerProcess, message etf.Term) gen.ServerStatus {
- state, ok := process.State.(*processState)
- if !ok {
- return gen.ServerStatusOK
- }
-
- switch m := message.(type) {
- case messageTimeServerReply:
- request, ok := state.asyncRequests[m.ref]
- if !ok {
- return gen.ServerStatusOK
- }
- delete(state.asyncRequests, m.ref)
- timeResult := fmt.Sprintf("time: %s", m.time)
- request.Response.Write([]byte(timeResult))
- process.Reply(m.ref, nil, nil)
-
- }
- return gen.ServerStatusOK
-}
diff --git a/examples/proxy/README.md b/examples/proxy/README.md
deleted file mode 100644
index 9a406058..00000000
--- a/examples/proxy/README.md
+++ /dev/null
@@ -1,24 +0,0 @@
-## Demo proxy connection
-
-This example demonstrates how to connect two nodes belonging to two different clusters by using the proxy feature and making this connection end-to-end encrypted.
-
-
-
-Here is output of this example
-
-```
-❯❯❯❯ go run .
-Starting node: node1 (cluster 1) ...OK
-Starting node: node2 (cluster 1) with Proxy.Transit = true ...OK
-Starting node: node3 (cluster 2) with Proxy.Transit = true ...OK
-Starting node: node4 (cluster 2) with Proxy.Cookie = "abc" ...OK
-Add static route on node2 to node3 with custom cookie to get access to the cluster 2 ...OK
-Add proxy route to node4 via node2 on node1 with proxy cookie = "abc" and enabled encryption ...OK
-Add proxy route to node4 via node3 on node2 ...OK
-Connect node1 to node4 ...OK
-Peers on node1 [node2@localhost node4@localhost]
-Peers on node2 [node1@localhost node3@localhost]
-Peers on node3 [node2@localhost node4@localhost]
-Peers on node4 [node3@localhost node1@localhost]
-```
-
diff --git a/examples/proxy/demo.png b/examples/proxy/demo.png
deleted file mode 100644
index c770f76c..00000000
Binary files a/examples/proxy/demo.png and /dev/null differ
diff --git a/examples/proxy/demo.svg b/examples/proxy/demo.svg
deleted file mode 100644
index 1aaefb24..00000000
--- a/examples/proxy/demo.svg
+++ /dev/null
@@ -1,433 +0,0 @@
-
-
-
-
diff --git a/examples/proxy/main.go b/examples/proxy/main.go
deleted file mode 100644
index 3a0cc5eb..00000000
--- a/examples/proxy/main.go
+++ /dev/null
@@ -1,96 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
-
- "github.com/ergo-services/ergo"
- "github.com/ergo-services/ergo/node"
-)
-
-func main() {
- flag.Parse()
-
- fmt.Printf("Starting node: node1 (cluster 1) ...")
- node1, err := ergo.StartNode("node1@localhost", "secret1", node.Options{})
- if err != nil {
- panic(err)
- }
- fmt.Println("OK")
-
- fmt.Printf("Starting node: node2 (cluster 1) with Proxy.Transit = true ...")
- opts2 := node.Options{}
- opts2.Proxy.Transit = true
- node2, err := ergo.StartNode("node2@localhost", "secret1", opts2)
- if err != nil {
- panic(err)
- }
- fmt.Println("OK")
-
- fmt.Printf("Starting node: node3 (cluster 2) with Proxy.Transit = true ...")
- opts3 := node.Options{}
- opts3.Proxy.Transit = true
- node3, err := ergo.StartNode("node3@localhost", "secret2", opts3)
- if err != nil {
- panic(err)
- }
- fmt.Println("OK")
-
- proxyCookie := "abc"
- fmt.Printf("Starting node: node4 (cluster 2) with Proxy.Cookie = %q ...", proxyCookie)
- opts4 := node.Options{}
- opts4.Proxy.Cookie = proxyCookie
- opts4.Proxy.Accept = true
- node4, err := ergo.StartNode("node4@localhost", "secret2", opts4)
- if err != nil {
- panic(err)
- }
- fmt.Println("OK")
-
- fmt.Printf("Add static route on node2 to node3 with custom cookie to get access to the cluster 2 ...")
- routeOptions := node.RouteOptions{
- Cookie: "secret2",
- }
- if err := node2.AddStaticRouteOptions(node3.Name(), routeOptions); err != nil {
- panic(err)
- }
- fmt.Println("OK")
-
- fmt.Printf("Add proxy route to node4 via node2 on node1 with proxy cookie = %q and enabled encryption ...", proxyCookie)
- proxyRoute1 := node.ProxyRoute{
- Name: node4.Name(),
- Proxy: node2.Name(),
- Cookie: proxyCookie,
- Flags: node.DefaultProxyFlags(),
- }
- proxyRoute1.Flags.EnableEncryption = true
- if err := node1.AddProxyRoute(proxyRoute1); err != nil {
- panic(err)
- }
- fmt.Println("OK")
-
- fmt.Printf("Add proxy route to node4 via node3 on node2 ...")
- proxyRoute2 := node.ProxyRoute{
- Name: node4.Name(),
- Proxy: node3.Name(),
- }
- if err := node2.AddProxyRoute(proxyRoute2); err != nil {
- panic(err)
- }
- fmt.Println("OK")
-
- fmt.Printf("Connect node1 to node4 ...")
- if err := node1.Connect(node4.Name()); err != nil {
- panic(err)
- }
- fmt.Println("OK")
- fmt.Println("Peers on node1", node1.Nodes())
- fmt.Println("Peers on node2", node2.Nodes())
- fmt.Println("Peers on node3", node3.Nodes())
- fmt.Println("Peers on node4", node4.Nodes())
-
- node1.Stop()
- node2.Stop()
- node3.Stop()
- node4.Stop()
-}
diff --git a/examples/supervisor/README.md b/examples/supervisor/README.md
deleted file mode 100644
index cf6eb413..00000000
--- a/examples/supervisor/README.md
+++ /dev/null
@@ -1,34 +0,0 @@
-## Supervisor demo scenario ##
-
-```
- demoSup
- |
- - demoServer01
- - demoServer02
- - demoServer03
-```
-
-Here is output of this example:
-```
-❯❯❯❯ go run .
-
-to stop press Ctrl-C
-
-Started new process
- Pid: <32747620.0.1012>
- Name: "demoServer01"
- Parent: <32747620.0.1011>
- Args:[]etf.Term(nil)
-Started new process
- Pid: <32747620.0.1013>
- Name: "demoServer02"
- Parent: <32747620.0.1011>
- Args:[]etf.Term{12345}
-Started new process
- Pid: <32747620.0.1014>
- Name: "demoServer03"
- Parent: <32747620.0.1011>
- Args:[]etf.Term{"abc", 67890}
-Started supervisor process <32747620.0.1011>
-
-```
diff --git a/examples/supervisor/main.go b/examples/supervisor/main.go
deleted file mode 100644
index 086e8a05..00000000
--- a/examples/supervisor/main.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
-
- "github.com/ergo-services/ergo"
- "github.com/ergo-services/ergo/gen"
- "github.com/ergo-services/ergo/node"
-)
-
-func main() {
- flag.Parse()
-
- fmt.Println("")
- fmt.Println("to stop press Ctrl-C")
- fmt.Println("")
-
- demoNode, err := ergo.StartNode("sup@localhost", "cookie", node.Options{})
- if err != nil {
- panic(err)
- }
-
- demoSup := createDemoSup()
- sup, err := demoNode.Spawn("demoSup", gen.ProcessOptions{}, demoSup)
- if err != nil {
- panic(err)
- }
- fmt.Println("Started supervisor process", sup.Self())
- demoNode.Wait()
-}
diff --git a/examples/supervisor/server.go b/examples/supervisor/server.go
deleted file mode 100644
index 22da0f07..00000000
--- a/examples/supervisor/server.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-func createDemoServer() gen.ServerBehavior {
- return &demoServer{}
-}
-
-type demoServer struct {
- gen.Server
-}
-
-func (ds *demoServer) Init(process *gen.ServerProcess, args ...etf.Term) error {
- fmt.Printf("Started new process\n\tPid: %s\n\tName: %q\n\tParent: %s\n\tArgs:%#v\n",
- process.Self(),
- process.Name(),
- process.Parent().Self(),
- args)
- return nil
-}
diff --git a/examples/supervisor/sup.go b/examples/supervisor/sup.go
deleted file mode 100644
index ae9f9d70..00000000
--- a/examples/supervisor/sup.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package main
-
-import (
- "github.com/ergo-services/ergo/etf"
- "github.com/ergo-services/ergo/gen"
-)
-
-func createDemoSup() gen.SupervisorBehavior {
- return &demoSup{}
-}
-
-type demoSup struct {
- gen.Supervisor
-}
-
-func (ds *demoSup) Init(args ...etf.Term) (gen.SupervisorSpec, error) {
- spec := gen.SupervisorSpec{
- Name: "demoAppSup",
- Children: []gen.SupervisorChildSpec{
- gen.SupervisorChildSpec{
- Name: "demoServer01",
- Child: createDemoServer(),
- },
- gen.SupervisorChildSpec{
- Name: "demoServer02",
- Child: createDemoServer(),
- Args: []etf.Term{12345},
- },
- gen.SupervisorChildSpec{
- Name: "demoServer03",
- Child: createDemoServer(),
- Args: []etf.Term{"abc", 67890},
- },
- },
- Strategy: gen.SupervisorStrategy{
- Type: gen.SupervisorStrategyOneForAll,
- Intensity: 2,
- Period: 5,
- Restart: gen.SupervisorStrategyRestartTemporary,
- },
- }
- return spec, nil
-}
diff --git a/gen/stage_dispatcher.go b/gen/stage_dispatcher.go
index ca153d4f..16659116 100644
--- a/gen/stage_dispatcher.go
+++ b/gen/stage_dispatcher.go
@@ -510,6 +510,7 @@ func (dp *dispatcherPartition) Subscribe(state interface{}, subscription StageSu
subscription: subscription,
minDemand: opts.MinDemand,
maxDemand: opts.MaxDemand,
+ partition: opts.Partition,
}
st.demands[subscription.Pid] = newDemand
st.order[opts.Partition] = append(st.order[opts.Partition], subscription.Pid)
diff --git a/gen/tcp.go b/gen/tcp.go
index b0fb8d46..cb6b61df 100644
--- a/gen/tcp.go
+++ b/gen/tcp.go
@@ -67,12 +67,8 @@ type TCPProcess struct {
listener net.Listener
}
-//
// Server callbacks
-//
func (tcp *TCP) Init(process *ServerProcess, args ...etf.Term) error {
-
- behavior := process.Behavior().(TCPBehavior)
behavior, ok := process.Behavior().(TCPBehavior)
if !ok {
return fmt.Errorf("not a TCPBehavior")
@@ -155,6 +151,21 @@ func (tcp *TCP) Init(process *ServerProcess, args ...etf.Term) error {
return nil
}
+func (tcp *TCP) HandleCall(process *ServerProcess, from ServerFrom, message etf.Term) (etf.Term, ServerStatus) {
+ tcpp := process.State.(*TCPProcess)
+ return tcpp.behavior.HandleTCPCall(tcpp, from, message)
+}
+
+func (tcp *TCP) HandleCast(process *ServerProcess, message etf.Term) ServerStatus {
+ tcpp := process.State.(*TCPProcess)
+ return tcpp.behavior.HandleTCPCast(tcpp, message)
+}
+
+func (tcp *TCP) HandleInfo(process *ServerProcess, message etf.Term) ServerStatus {
+ tcpp := process.State.(*TCPProcess)
+ return tcpp.behavior.HandleTCPInfo(tcpp, message)
+}
+
func (tcp *TCP) Terminate(process *ServerProcess, reason string) {
p := process.State.(*TCPProcess)
p.listener.Close()
@@ -326,9 +337,6 @@ retry:
lib.Warning("[gen.TCP] error on handling packet: %s. closing connection with %q", err, c.RemoteAddr())
return err
}
-
- expectingBytes = 1
- goto nextPacket
}
// create a new handler. we should eather to make a call HandleDisconnect or
diff --git a/gen/types.go b/gen/types.go
index d2f15551..473ebb63 100644
--- a/gen/types.go
+++ b/gen/types.go
@@ -289,7 +289,7 @@ type ProcessState struct {
State interface{}
}
-// ProcessBehavior interface contains methods you should implement to make own process behaviour
+// ProcessBehavior interface contains methods you should implement to make your own process behavior
type ProcessBehavior interface {
ProcessInit(Process, ...etf.Term) (ProcessState, error)
ProcessLoop(ProcessState, chan<- bool) string // method which implements control flow of process
@@ -357,10 +357,10 @@ func (p ProcessID) String() string {
// MessageDown delivers as a message to Server's HandleInfo callback of the process
// that created monitor using MonitorProcess.
// Reason values:
-// - the exit reason of the process
-// - 'noproc' (process did not exist at the time of monitor creation)
-// - 'noconnection' (no connection to the node where the monitored process resides)
-// - 'noproxy' (no connection to the proxy this node had has a connection through. monitored process could be still alive)
+// - the exit reason of the process
+// - 'noproc' (process did not exist at the time of monitor creation)
+// - 'noconnection' (no connection to the node where the monitored process resides)
+// - 'noproxy' (no connection to the proxy this node had has a connection through. monitored process could be still alive)
type MessageDown struct {
Ref etf.Ref // a monitor reference
ProcessID ProcessID // if monitor was created by name
@@ -387,10 +387,10 @@ type MessageProxyDown struct {
// MessageExit delievers to Server's HandleInfo callback on enabled trap exit using SetTrapExit(true)
// Reason values:
-// - the exit reason of the process
-// - 'noproc' (process did not exist at the time of link creation)
-// - 'noconnection' (no connection to the node where the linked process resides)
-// - 'noproxy' (no connection to the proxy this node had has a connection through. linked process could be still alive)
+// - the exit reason of the process
+// - 'noproc' (process did not exist at the time of link creation)
+// - 'noconnection' (no connection to the node where the linked process resides)
+// - 'noproxy' (no connection to the proxy this node had has a connection through. linked process could be still alive)
type MessageExit struct {
Pid etf.Pid
Reason string
diff --git a/gen/udp.go b/gen/udp.go
index 929f84b4..fcee44cb 100644
--- a/gen/udp.go
+++ b/gen/udp.go
@@ -66,9 +66,7 @@ type UDPPacket struct {
Socket io.Writer
}
-//
// Server callbacks
-//
func (udp *UDP) Init(process *ServerProcess, args ...etf.Term) error {
behavior := process.Behavior().(UDPBehavior)
@@ -129,6 +127,22 @@ func (udp *UDP) Init(process *ServerProcess, args ...etf.Term) error {
go udpProcess.serve()
return nil
}
+
+func (udp *UDP) HandleCall(process *ServerProcess, from ServerFrom, message etf.Term) (etf.Term, ServerStatus) {
+ udpp := process.State.(*UDPProcess)
+ return udpp.behavior.HandleUDPCall(udpp, from, message)
+}
+
+func (udp *UDP) HandleCast(process *ServerProcess, message etf.Term) ServerStatus {
+ udpp := process.State.(*UDPProcess)
+ return udpp.behavior.HandleUDPCast(udpp, message)
+}
+
+func (udp *UDP) HandleInfo(process *ServerProcess, message etf.Term) ServerStatus {
+ udpp := process.State.(*UDPProcess)
+ return udpp.behavior.HandleUDPInfo(udpp, message)
+}
+
func (udp *UDP) Terminate(process *ServerProcess, reason string) {
p := process.State.(*UDPProcess)
p.packetConn.Close()
diff --git a/lib/cert.go b/lib/cert.go
index ada1d0fa..4f210cc0 100644
--- a/lib/cert.go
+++ b/lib/cert.go
@@ -10,12 +10,13 @@ import (
"crypto/x509/pkix"
"encoding/pem"
"math/big"
+ "net"
"sync"
"time"
)
// GenerateSelfSignedCert
-func GenerateSelfSignedCert(org string) (tls.Certificate, error) {
+func GenerateSelfSignedCert(org string, hosts ...string) (tls.Certificate, error) {
var cert = tls.Certificate{}
certPrivKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
@@ -41,6 +42,14 @@ func GenerateSelfSignedCert(org string) (tls.Certificate, error) {
BasicConstraintsValid: true,
}
+ for _, h := range hosts {
+ if ip := net.ParseIP(h); ip != nil {
+ template.IPAddresses = append(template.IPAddresses, ip)
+ } else {
+ template.DNSNames = append(template.DNSNames, h)
+ }
+ }
+
certBytes, err1 := x509.CreateCertificate(rand.Reader, &template, &template,
&certPrivKey.PublicKey, certPrivKey)
if err1 != nil {
diff --git a/lib/errors.go b/lib/errors.go
index b1c3450a..74259fe4 100644
--- a/lib/errors.go
+++ b/lib/errors.go
@@ -40,13 +40,14 @@ var (
ErrUnsupportedRequest = fmt.Errorf("unsupported request")
ErrServerTerminated = fmt.Errorf("server terminated")
- ErrProxyUnknownRequest = fmt.Errorf("unknown proxy request")
- ErrProxyTransitDisabled = fmt.Errorf("proxy feature disabled")
- ErrProxyNoRoute = fmt.Errorf("no proxy route to node")
- ErrProxyConnect = fmt.Errorf("can't establish proxy connection")
- ErrProxyHopExceeded = fmt.Errorf("proxy hop is exceeded")
- ErrProxyLoopDetected = fmt.Errorf("proxy loop detected")
- ErrProxyPathTooLong = fmt.Errorf("proxy path too long")
- ErrProxySessionUnknown = fmt.Errorf("unknown session id")
- ErrProxySessionDuplicate = fmt.Errorf("session is already exist")
+ ErrProxyUnknownRequest = fmt.Errorf("unknown proxy request")
+ ErrProxyTransitDisabled = fmt.Errorf("proxy feature disabled")
+ ErrProxyTransitRestricted = fmt.Errorf("proxy connect restricted")
+ ErrProxyNoRoute = fmt.Errorf("no proxy route to node")
+ ErrProxyConnect = fmt.Errorf("can't establish proxy connection")
+ ErrProxyHopExceeded = fmt.Errorf("proxy hop is exceeded")
+ ErrProxyLoopDetected = fmt.Errorf("proxy loop detected")
+ ErrProxyPathTooLong = fmt.Errorf("proxy path too long")
+ ErrProxySessionUnknown = fmt.Errorf("unknown session id")
+ ErrProxySessionDuplicate = fmt.Errorf("session is already exist")
)
diff --git a/lib/tools.go b/lib/tools.go
index 0396decb..a903394d 100644
--- a/lib/tools.go
+++ b/lib/tools.go
@@ -9,6 +9,10 @@ import (
"io"
"log"
"math"
+ "path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
"sync"
"time"
)
@@ -23,6 +27,7 @@ var (
ergoTrace = false
ergoWarning = false
ergoNoRecover = false
+ ergoDebug = false
DefaultBufferLength = 16384
buffers = &sync.Pool{
@@ -47,22 +52,43 @@ var (
)
func init() {
- flag.BoolVar(&ergoTrace, "ergo.trace", false, "enable/disable extended debug info")
+ flag.BoolVar(&ergoTrace, "ergo.trace", false, "enable/disable extended node logging info")
flag.BoolVar(&ergoWarning, "ergo.warning", true, "enable/disable warning messages")
flag.BoolVar(&ergoNoRecover, "ergo.norecover", false, "disable panic catching")
+ flag.BoolVar(&ergoDebug, "ergo.debug", false, "enable/disable debug messages")
}
// Log
func Log(f string, a ...interface{}) {
if ergoTrace {
- log.Printf(f, a...)
+ printf(f, a...)
}
}
// Warning
func Warning(f string, a ...interface{}) {
if ergoWarning {
- log.Printf("WARNING! "+f, a...)
+ printf("WARNING! "+f, a...)
+ }
+}
+
+// Print log information
+func printf(formating string, args ...interface{}) {
+ if ergoDebug {
+ goID, fileName, line, funcName := 0, "unknown", 0, "unknown"
+ var buf [64]byte
+ n := runtime.Stack(buf[:], false)
+ idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
+ goID, _ = strconv.Atoi(idField)
+ pc, fileName, line, ok := runtime.Caller(2)
+ if ok {
+ funcName = runtime.FuncForPC(pc).Name()
+ funcName = filepath.Base(funcName)
+ fileName = filepath.Base(fileName)
+ }
+ log.Printf("@%s:%d %s %s\n", goID, fileName, line, funcName, fmt.Sprintf(formating, args...))
+ } else {
+ log.Printf(formating, args...)
}
}
@@ -78,7 +104,12 @@ func TakeTimer() *time.Timer {
// ReleaseTimer
func ReleaseTimer(t *time.Timer) {
- t.Stop()
+ if !t.Stop() {
+ select {
+ case <-t.C:
+ default:
+ }
+ }
timers.Put(t)
}
diff --git a/node/monitor.go b/node/monitor.go
index cac9d718..63843fb7 100644
--- a/node/monitor.go
+++ b/node/monitor.go
@@ -1082,6 +1082,7 @@ func (m *monitor) sendEvent(by etf.Pid, event gen.Event, message gen.EventMessag
return lib.ErrEventMismatch
}
+ // TODO clean up terminated subscribers
for _, pid := range item.monitors {
m.router.RouteSend(etf.Pid{}, pid, message)
}
diff --git a/node/network.go b/node/network.go
index fb6ea775..6a40692b 100644
--- a/node/network.go
+++ b/node/network.go
@@ -42,6 +42,7 @@ type networkInternal interface {
ProxyRoutes() []ProxyRoute
ProxyRoute(name string) (ProxyRoute, bool)
+ Registrar() Registrar
Resolve(peername string) (Route, error)
ResolveProxy(peername string) (ProxyRoute, error)
@@ -79,6 +80,8 @@ type connectionInternal struct {
connection ConnectionInterface
//
proxySessionID string
+ //
+ proxyTransitTo map[string]bool
}
type network struct {
@@ -145,7 +148,7 @@ func newNetwork(ctx context.Context, nodename string, cookie string, options Opt
}
splitNodeHost := strings.Split(nodename, "@")
- if len(splitNodeHost) != 2 {
+ if len(splitNodeHost) != 2 || splitNodeHost[0] == "" || splitNodeHost[1] == "" {
return nil, fmt.Errorf("FQDN for node name is required (example: node@hostname)")
}
@@ -417,6 +420,11 @@ func (n *network) ResolveProxy(name string) (ProxyRoute, error) {
return route, nil
}
+// Registrar
+func (n *network) Registrar() Registrar {
+ return n.registrar
+}
+
// Connect
func (n *network) Connect(node string) error {
_, err := n.getConnection(node)
@@ -492,6 +500,7 @@ func (n *network) RouteProxyConnectRequest(from ConnectionInterface, request Pro
if request.To != n.nodename {
var err error
var connection ConnectionInterface
+ var proxyTransitTo map[string]bool
//
// outgoing proxy request
//
@@ -500,6 +509,7 @@ func (n *network) RouteProxyConnectRequest(from ConnectionInterface, request Pro
n.connectionsMutex.RLock()
if ci, exist := n.connections[request.To]; exist {
connection = ci.connection
+ proxyTransitTo = ci.proxyTransitTo
}
n.connectionsMutex.RUnlock()
@@ -518,6 +528,17 @@ func (n *network) RouteProxyConnectRequest(from ConnectionInterface, request Pro
lib.Log("[%s] NETWORK proxy. Proxy feature is disabled on this node", n.nodename)
return lib.ErrProxyTransitDisabled
}
+
+ if proxyTransitTo != nil {
+ if proxyTransitTo[request.To] == false {
+ nodeHost := strings.Split(request.To, "@")
+ if len(nodeHost) != 2 || proxyTransitTo[nodeHost[1]] == false {
+ lib.Log("[%s] NETWORK proxy. Proxy connection is restricted (to: %s)", n.nodename, request.To)
+ return lib.ErrProxyTransitRestricted
+ }
+ }
+ }
+
if request.Hop < 1 {
lib.Log("[%s] NETWORK proxy. Error: exceeded hop limit", n.nodename)
return lib.ErrProxyHopExceeded
@@ -1067,7 +1088,12 @@ func (n *network) listen(ctx context.Context, hostname string, options Listener,
}
for port := options.ListenBegin; port <= options.ListenEnd; port++ {
+ if options.Hostname != "" {
+ hostname = options.Hostname
+ }
+
hostPort := net.JoinHostPort(hostname, strconv.Itoa(int(port)))
+ lib.Log("[%s] NETWORK trying to start listener on %q", n.nodename, hostPort)
listener, err := lc.Listen(ctx, "tcp", hostPort)
if err != nil {
continue
@@ -1086,7 +1112,7 @@ func (n *network) listen(ctx context.Context, hostname string, options Listener,
if err := n.registrar.Register(n.ctx, n.nodename, registerOptions); err != nil {
listener.Close()
- return nil, err
+ return nil, fmt.Errorf("can not register this node: %s", err)
}
}
@@ -1135,6 +1161,13 @@ func (n *network) listen(ctx context.Context, hostname string, options Listener,
connection: connection,
}
+ if len(details.ProxyTransit.AllowTo) > 0 {
+ cInternal.proxyTransitTo = make(map[string]bool)
+ for _, to := range details.ProxyTransit.AllowTo {
+ cInternal.proxyTransitTo[to] = true
+ }
+ }
+
if _, err := n.registerConnection(details.Name, cInternal); err != nil {
// Race condition:
// There must be another goroutine which already created and registered
@@ -1177,6 +1210,7 @@ func (n *network) connect(node string) (ConnectionInterface, error) {
HostPort := net.JoinHostPort(route.Host, strconv.Itoa(int(route.Port)))
dialer := net.Dialer{
KeepAlive: defaultKeepAlivePeriod * time.Second,
+ Timeout: 3 * time.Second, // timeout to establish TCP-connection
}
tlsEnabled := route.Options.TLS != nil
@@ -1343,9 +1377,6 @@ func (n *network) unregisterConnection(peername string, disconnect *ProxyDisconn
}
n.router.sendEvent(corePID, EventNetwork, event)
- // we must unregister this peer for the proxy connection via this node
- n.registrar.UnregisterProxy(peername)
-
n.connectionsMutex.Lock()
cp, _ := n.connectionsProxy[ci.connection]
for _, p := range cp {
@@ -1383,9 +1414,7 @@ func (n *network) unregisterConnection(peername string, disconnect *ProxyDisconn
}
-//
// Connection interface default callbacks
-//
func (c *Connection) Send(from gen.Process, to etf.Pid, message etf.Term) error {
return lib.ErrUnsupported
}
@@ -1453,9 +1482,7 @@ func (c *Connection) Stats() NetworkStats {
return NetworkStats{}
}
-//
// Handshake interface default callbacks
-//
func (h *Handshake) Start(remote net.Addr, conn lib.NetReadWriter, tls bool, cookie string) (HandshakeDetails, error) {
return HandshakeDetails{}, lib.ErrUnsupported
}
diff --git a/node/types.go b/node/types.go
index cedfd82d..7809906a 100644
--- a/node/types.go
+++ b/node/types.go
@@ -104,6 +104,9 @@ type Node interface {
// If it wasn't found makes request to the registrar.
ResolveProxy(node string) (ProxyRoute, error)
+ // Returns Registrar interface
+ Registrar() Registrar
+
// Connect sets up a connection to node
Connect(node string) error
// Disconnect close connection to the node
@@ -244,6 +247,8 @@ type Listener struct {
// Cookie cookie for the incoming connection to this listener. Leave it empty in
// case of using the node's cookie.
Cookie string
+ // Hostname defines an interface for the listener. Default: takes from the node name.
+ Hostname string
// Listen defines a listening port number for accepting incoming connections.
Listen uint16
// ListenBegin and ListenEnd define a range of the port numbers where
@@ -375,7 +380,11 @@ type HandshakeDetails struct {
// NumHandlers defines the number of readers/writers per connection. Default value is provided by ProtoOptions
NumHandlers int
// AtomMapping
- AtomMapping etf.AtomMapping
+ AtomMapping *etf.AtomMapping
+ // ProxyTransit allows to restrict proxy connection requests for this connection
+ ProxyTransit ProxyTransit
+ // Buffer keeps data received along with the handshake
+ Buffer *lib.Buffer
// Custom allows passing the custom data to the ProtoInterface.Start
Custom HandshakeCustomDetails
}
@@ -448,11 +457,13 @@ type Registrar interface {
Resolve(peername string) (Route, error)
ResolveProxy(peername string) (ProxyRoute, error)
Config() (RegistrarConfig, error)
+ ConfigItem(name string) (etf.Term, error)
+ SetConfigUpdateCallback(func(name string, value etf.Term) error) error
}
type RegistrarConfig struct {
Version int
- Config map[string]etf.Term
+ Config etf.Term
}
// RegisterOptions defines resolving options
@@ -511,6 +522,11 @@ type ProxyFlags struct {
EnableEncryption bool
}
+// ProxyTransit
+type ProxyTransit struct {
+ AllowTo []string
+}
+
// ProxyConnectRequest
type ProxyConnectRequest struct {
ID etf.Ref
diff --git a/proto/dist/handshake.go b/proto/dist/handshake.go
index 54d9ca67..f47da79e 100644
--- a/proto/dist/handshake.go
+++ b/proto/dist/handshake.go
@@ -246,7 +246,7 @@ func (dh *DistHandshake) Start(remote net.Addr, conn lib.NetReadWriter, tls bool
case 'a':
// 'a' + 16 (digest)
- if len(buffer) != 17 {
+ if len(buffer) < 17 {
return details, fmt.Errorf("malformed handshake ('a' length of digest)")
}
@@ -256,6 +256,12 @@ func (dh *DistHandshake) Start(remote net.Addr, conn lib.NetReadWriter, tls bool
return details, fmt.Errorf("malformed handshake ('a' digest)")
}
+ // check if we got DIST packet with the final handshake data.
+ if len(buffer) > 17 {
+ details.Buffer = lib.TakeBuffer()
+ details.Buffer.Set(buffer[17:])
+ }
+
// handshaked
return details, nil
@@ -785,12 +791,16 @@ func composeFlags(flags node.Flags) nodeFlags {
flagPublished,
flagUnicodeIO,
flagDistMonitor,
+ flagNewFloats,
+ flagBitBinaries,
flagDistMonitorName,
flagExtendedPidsPorts,
flagExtendedReferences,
flagAtomCache,
flagHiddenAtomCache,
+ flagFunTags,
flagNewFunTags,
+ flagExportPtrTag,
flagSmallAtomTags,
flagUTF8Atoms,
flagMapTag,
diff --git a/proto/dist/proto.go b/proto/dist/proto.go
index 0294453a..91a1685e 100644
--- a/proto/dist/proto.go
+++ b/proto/dist/proto.go
@@ -113,6 +113,9 @@ type distConnection struct {
// writer
flusher *linkFlusher
+ // buffer
+ buffer *lib.Buffer
+
// senders list of channels for the sending goroutines
senders senders
// receivers list of channels for the receiving goroutines
@@ -121,7 +124,7 @@ type distConnection struct {
// atom cache for outgoing messages
cache etf.AtomCache
- mapping etf.AtomMapping
+ mapping *etf.AtomMapping
// fragmentation sequence ID
sequenceID int64
@@ -187,6 +190,7 @@ func (dp *distProto) Init(ctx context.Context, conn lib.NetReadWriter, nodename
peername: details.Name,
flags: details.Flags,
creation: details.Creation,
+ buffer: details.Buffer,
conn: conn,
cache: etf.NewAtomCache(),
mapping: details.AtomMapping,
@@ -253,7 +257,11 @@ func (dp *distProto) Serve(ci node.ConnectionInterface, router node.CoreRouter)
var err error
var packetLength int
- b := lib.TakeBuffer()
+ b := connection.buffer // not nil if we got extra data withing the handshake process
+ if b == nil {
+ b = lib.TakeBuffer()
+ }
+
for {
packetLength, err = connection.read(b, dp.options.MaxMessageSize)
@@ -927,6 +935,8 @@ func (dc *distConnection) decodePacket(b *lib.Buffer) (*distMessage, error) {
dc.ProxyDisconnect(disconnect)
return nil, nil
}
+
+ // BUG? double counted. see below
atomic.AddUint64(&dc.stats.BytesIn, uint64(b.Len()))
iv := packet[:aes.BlockSize]
@@ -969,6 +979,7 @@ func (dc *distConnection) decodePacket(b *lib.Buffer) (*distMessage, error) {
dc.ProxyDisconnect(disconnect)
return nil, nil
}
+ // BUG? double counted. see above
atomic.AddUint64(&dc.stats.BytesIn, uint64(b.Len()))
if control == nil {
return nil, nil
diff --git a/proto/dist/registrar.go b/proto/dist/registrar.go
index 3bafeee2..3d4e7d9f 100644
--- a/proto/dist/registrar.go
+++ b/proto/dist/registrar.go
@@ -9,8 +9,10 @@ import (
"net"
"strconv"
"strings"
+ "sync/atomic"
"time"
+ "github.com/ergo-services/ergo/etf"
"github.com/ergo-services/ergo/lib"
"github.com/ergo-services/ergo/node"
)
@@ -49,7 +51,8 @@ type epmdRegistrar struct {
nodeHost string
handshakeVersion node.HandshakeVersion
- extra []byte
+ running int32
+ extra []byte
}
func CreateRegistrar() node.Registrar {
@@ -83,6 +86,10 @@ func CreateRegistrarWithRemoteEPMD(host string, port uint16) node.Registrar {
}
func (e *epmdRegistrar) Register(ctx context.Context, name string, options node.RegisterOptions) error {
+ if atomic.CompareAndSwapInt32(&e.running, 0, 1) == false {
+ return fmt.Errorf("registrar is already running")
+ }
+
n := strings.Split(name, "@")
if len(n) != 2 {
return fmt.Errorf("(EMPD) FQDN for node name is required (example: node@hostname)")
@@ -98,6 +105,7 @@ func (e *epmdRegistrar) Register(ctx context.Context, name string, options node.
ready := make(chan error)
go func() {
+ defer atomic.StoreInt32(&e.running, 0)
buf := make([]byte, 16)
var reconnecting bool
@@ -189,8 +197,15 @@ func (e *epmdRegistrar) UnregisterProxy(name string) error {
return lib.ErrUnsupported
}
func (e *epmdRegistrar) Config() (node.RegistrarConfig, error) {
- var cfg node.RegistrarConfig
- return cfg, lib.ErrUnsupported
+ return node.RegistrarConfig{}, lib.ErrUnsupported
+}
+func (e *epmdRegistrar) ConfigItem(name string) (etf.Term, error) {
+ return nil, lib.ErrUnsupported
+}
+
+// just stub
+func (e *epmdRegistrar) SetConfigUpdateCallback(func(string, etf.Term) error) error {
+ return lib.ErrUnsupported
}
func (e *epmdRegistrar) composeExtra(options node.RegisterOptions) {