Skip to content

Commit

Permalink
V220 (#110)
Browse files Browse the repository at this point in the history
  • Loading branch information
halturin authored Oct 18, 2022
1 parent d9bfaba commit 093c5d1
Show file tree
Hide file tree
Showing 136 changed files with 8,010 additions and 1,935 deletions.
Binary file removed .github/images/observer.gif
Binary file not shown.
63 changes: 63 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,69 @@ 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.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.
* 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
* Updated `gen.ServerBehavior.HandleDirect` method (got extra argument `etf.Ref` to distinguish the requests). This change allows you to handle these requests asynchronously using method `gen.ServerProcess.Reply(...)`
* Updated `node.Options`. Now it has field `Listeners` (type `node.Listener`). It allows you to start any number of listeners with custom options - `Port`, `TLS` settings, or custom `Handshake`/`Proto` interfaces
* Fixed build on 32-bit arch
* Fixed freezing on ARM arch #102
* Fixed problem with encoding negative int8
* Fixed #103 (there was an issue on interop with Elixir's GenStage)
* Fixed node stuck on start if it uses the name which is already taken in EPMD
* Fixed incorrect `gen.ProcessOptions.Context` handling


#### [v2.1.0](https://github.com/ergo-services/ergo/releases/tag/v1.999.210) 2022-04-19 [tag version v1.999.210] ####

* Introduced **compression feature** support. Here are new methods and options to manage this feature:
- `gen.Process`:
- `SetCompression(enable bool)`, `Compression() bool`
- `SetCompressionLevel(level int) bool`, `CompressionLevel() int`
- `SetCompressionThreshold(threshold int) bool`, `CompressionThreshold() int` messages smaller than the threshold will be sent with no compression. The default compression threshold is 1024 bytes.
- `node.Options`:
- `Compression` these settings are used as defaults for the spawning processes
- this feature will be ignored if the receiver is running on either the Erlang or Elixir node
* Introduced **proxy feature** support **with end-to-end encryption**.
- `node.Node` new methods:
- `AddProxyRoute(...)`, `RemoveProxyRoute(...)`
- `ProxyRoute(...)`, `ProxyRoutes()`
- `NodesIndirect()` returns list of connected nodes via proxy connection
- `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).
- 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 **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
- `Proto` provides the way to implement proprietary protocols (e.g., IoT area)
* Other new features:
- `gen.Process` new methods:
- `NodeUptime()`, `NodeName()`, `NodeStop()`
- `gen.ServerProcess` new method:
- `MessageCounter()` shows how many messages have been handled by the `gen.Server` callbacks
- `gen.ProcessOptions` new option:
- `ProcessFallback` allows forward messages to the fallback process if the process mailbox is full. Forwarded messages are wrapped into `gen.MessageFallback` struct. Related to issue #96.
- `gen.SupervisorChildSpec` and `gen.ApplicationChildSpec` got option `gen.ProcessOptions` to customize options for the spawning child processes.
* Improved sending messages by etf.Pid or etf.Alias: methods `gen.Process.Send`, `gen.ServerProcess.Cast`, `gen.ServerProcess.Call` now return `node.ErrProcessIncarnation` if a message is sending to the remote process of the previous incarnation (remote node has been restarted). Making monitor on a remote process of the previous incarnation triggers sending `gen.MessageDown` with reason `incarnation`.
* Introduced type `gen.EnvKey` for the environment variables
* All spawned processes now have the `node.EnvKeyNode` variable to get access to the `node.Node` value.
* **Improved performance** of local messaging (**up to 8 times** for some cases)
* **Important** `node.Options` has changed. Make sure to adjust your code.
* Fixed issue #89 (incorrect handling of Call requests)
* Fixed issues #87, #88 and #93 (closing network socket)
* Fixed issue #96 (silently drops message if process mailbox is full)
* Updated minimal requirement of Golang version to 1.17 (go.mod)
* We still keep the rule **Zero Dependencies**

#### [v2.0.0](https://github.com/ergo-services/ergo/releases/tag/v1.999.200) 2021-10-12 [tag version v1.999.200] ####

* 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)
Expand Down
111 changes: 31 additions & 80 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@

<!--[![Gitbook Documentation](https://img.shields.io/badge/GitBook-Documentation-f37f40?style=plastic&logo=gitbook&logoColor=white&style=flat)](https://docs.ergo.services) -->
[![GoDoc](https://pkg.go.dev/badge/ergo-services/ergo)](https://pkg.go.dev/github.com/ergo-services/ergo)
[![Build Status](https://img.shields.io/github/workflow/status/ergo-services/ergo/TestLinuxWindowsMacOS)](https://github.com/ergo-services/ergo/actions/)
[![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT)
[![Telegram Community](https://img.shields.io/badge/Telegram-Community-blue?style=flat&logo=telegram)](https://t.me/ergo_services)
[![Discord Community](https://img.shields.io/badge/Discord-Community-5865F2?style=flat&logo=discord&logoColor=white)](https://discord.gg/sdscxKGV62)
[![Twitter](https://img.shields.io/badge/Twitter-ergo__services-1DA1F2?style=flat&logo=twitter&logoColor=white)](https://twitter.com/ergo_services)
[![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT)

Technologies and design patterns of Erlang/OTP have been proven over the years. Now in Golang.
Up to x5 times faster than original Erlang/OTP in terms of network messaging.
Expand Down Expand Up @@ -37,7 +36,7 @@ The goal of this project is to leverage Erlang/OTP experience with Golang perfor
* 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/raft](examples/raft).
* `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).
* 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
Expand All @@ -64,50 +63,23 @@ 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.1.0](https://github.com/ergo-services/ergo/releases/tag/v1.999.210) 2022-04-19 [tag version v1.999.210] ####

* Introduced **compression feature** support. Here are new methods and options to manage this feature:
- `gen.Process`:
- `SetCompression(enable bool)`, `Compression() bool`
- `SetCompressionLevel(level int) bool`, `CompressionLevel() int`
- `SetCompressionThreshold(threshold int) bool`, `CompressionThreshold() int` messages smaller than the threshold will be sent with no compression. The default compression threshold is 1024 bytes.
- `node.Options`:
- `Compression` these settings are used as defaults for the spawning processes
- this feature will be ignored if the receiver is running on either the Erlang or Elixir node
* Introduced **proxy feature** support **with end-to-end encryption**.
- `node.Node` new methods:
- `AddProxyRoute(...)`, `RemoveProxyRoute(...)`
- `ProxyRoute(...)`, `ProxyRoutes()`
- `NodesIndirect()` returns list of connected nodes via proxy connection
- `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).
- 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/raft](examples/raft).
* 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
- `Proto` provides the way to implement proprietary protocols (e.g., IoT area)
* Other new features:
- `gen.Process` new methods:
- `NodeUptime()`, `NodeName()`, `NodeStop()`
- `gen.ServerProcess` new method:
- `MessageCounter()` shows how many messages have been handled by the `gen.Server` callbacks
- `gen.ProcessOptions` new option:
- `ProcessFallback` allows forward messages to the fallback process if the process mailbox is full. Forwarded messages are wrapped into `gen.MessageFallback` struct. Related to issue #96.
- `gen.SupervisorChildSpec` and `gen.ApplicationChildSpec` got option `gen.ProcessOptions` to customize options for the spawning child processes.
* Improved sending messages by etf.Pid or etf.Alias: methods `gen.Process.Send`, `gen.ServerProcess.Cast`, `gen.ServerProcess.Call` now return `node.ErrProcessIncarnation` if a message is sending to the remote process of the previous incarnation (remote node has been restarted). Making monitor on a remote process of the previous incarnation triggers sending `gen.MessageDown` with reason `incarnation`.
* Introduced type `gen.EnvKey` for the environment variables
* All spawned processes now have the `node.EnvKeyNode` variable to get access to the `node.Node` value.
* **Improved performance** of local messaging (**up to 8 times** for some cases)
* **Important** `node.Options` has changed. Make sure to adjust your code.
* Fixed issue #89 (incorrect handling of Call requests)
* Fixed issues #87, #88 and #93 (closing network socket)
* Fixed issue #96 (silently drops message if process mailbox is full)
* Updated minimal requirement of Golang version to 1.17 (go.mod)
* We still keep the rule **Zero Dependencies**

#### [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 **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
* Updated `gen.ServerBehavior.HandleDirect` method (got extra argument `etf.Ref` to distinguish the requests). This change allows you to handle these requests asynchronously using method `gen.ServerProcess.Reply(...)`
* Updated `node.Options`. Now it has field `Listeners` (type `node.Listener`). It allows you to start any number of listeners with custom options - `Port`, `TLS` settings, or custom `Handshake`/`Proto` interfaces
* Fixed build on 32-bit arch
* Fixed freezing on ARM arch #102
* Fixed problem with encoding negative int8
* Fixed #103 (there was an issue on interop with Elixir's GenStage)
* Fixed node stuck on start if it uses the name which is already taken in EPMD
* Fixed incorrect `gen.ProcessOptions.Context` handling

### Benchmarks ###

Expand Down Expand Up @@ -183,17 +155,9 @@ sources of these benchmarks are [here](https://github.com/halturin/ergobenchmark

The one thing that makes embedded EPMD different is the behavior of handling connection hangs - if ergo' node is running as an EPMD client and lost connection, it tries either to run its own embedded EPMD service or to restore the lost connection.

### Observer ###

It's a standard Erlang tool. Observer is a graphical tool for observing the characteristics of Erlang systems. The tool Observer displays system information, application supervisor trees, process information.

Here you can see this feature in action using one of the [examples](examples/):

![observer demo](.github/images/observer.gif)

### Examples ###

Code below is a simple implementation of gen.Server pattern [examples/simple](examples/simple)
Code below is a simple implementation of gen.Server pattern [examples/genserver](examples/genserver)

```golang
package main
Expand All @@ -202,13 +166,10 @@ 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"
)

// simple implementation of Server
type simple struct {
gen.Server
}
Expand All @@ -219,27 +180,12 @@ func (s *simple) HandleInfo(process *gen.ServerProcess, message etf.Term) gen.Se
if value > 104 {
return gen.ServerStatusStop
}
// sending message with delay
process.SendAfter(process.Self(), value+1, time.Duration(1*time.Second))
// 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
}

func main() {
// create a new node
node, _ := ergo.StartNode("node@localhost", "cookies", node.Options{})

// spawn a new process of gen.Server
process, _ := node.Spawn("gs1", gen.ProcessOptions{}, &simple{})

// send a message to itself
process.Send(process.Self(), 100)

// wait for the process termination.
process.Wait()
fmt.Println("exited")
node.Stop()
}

```

here is output of this code
Expand All @@ -262,9 +208,14 @@ See `examples/` for more details
* [gen.Server](examples/genserver)
* [gen.Stage](examples/genstage)
* [gen.Saga](examples/gensaga)
* [gen.Demo](examples/gendemo)
* [Node with TLS](examples/nodetls)
* [Node with HTTP server](examples/http)
* [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)

### Elixir Phoenix Users ###

Expand Down
Loading

0 comments on commit 093c5d1

Please sign in to comment.