Skip to content

Commit 79bebaa

Browse files
authored
V210 (#98)
* Custom handshake/proro/resolver (#80) * Compression feature (#85) * Fix DIST connection reader * fix issue #89 (#90) * fix issue87 * sync * Proxy (#97) * Raft (#99) Raft implementation * update README.md * update gen/README.md * add github template * fix conflict
1 parent e9c6905 commit 79bebaa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+16019
-5057
lines changed

.github/workflows/testLinuxWindowsMacOS.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
- name: Set up Go
1717
uses: actions/setup-go@v2
1818
with:
19-
go-version: 1.16
19+
go-version: 1.17
2020

2121
- name: Test
2222
run: go test -v ./...
@@ -29,7 +29,7 @@ jobs:
2929
- name: Set up Go
3030
uses: actions/setup-go@v2
3131
with:
32-
go-version: 1.16
32+
go-version: 1.17
3333

3434
- name: Test
3535
run: go test -v ./...
@@ -42,7 +42,7 @@ jobs:
4242
- name: Set up Go
4343
uses: actions/setup-go@v2
4444
with:
45-
go-version: 1.16
45+
go-version: 1.17
4646

4747
- name: Test
4848
run: go test -v ./...

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
*~
2-
cmd/epmd/epmd
32
coverage.txt
43
coverage.html
4+
tests/coverage.txt
5+
tests/coverage.html
56
*.swp
67
tags
78
.session
89
cover.out
10+
tests/cover.out
11+
examples/sandbox
912

ChangeLog.md

+25
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,31 @@ All notable changes to this project will be documented in this file.
44
This format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
#### [v2.0.0](https://github.com/ergo-services/ergo/releases/tag/v1.999.200) 2021-10-12 [tag version v1.999.200] ####
8+
9+
* 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)
10+
* **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.
11+
* **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).
12+
* 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).
13+
* 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.
14+
* 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.
15+
* Introduced new types intended to be used to interact with Erlang/Elixir
16+
* `etf.ListImproper` to support improper lists like `[a|b]` (a cons cell).
17+
* `etf.String` (an alias for the Golang string) encodes as a binary in order to support Elixir string type (which is `binary()` type)
18+
* `etf.Charlist` (an alias for the Golang string) encodes as a list of chars `[]rune` in order to support Erlang string type (which is `charlist()` type)
19+
* Introduced new methods `Node.ProvideRemoteSpawn`, `Node.RevokeRemoteSpawn`, `Process.RemoteSpawn`.
20+
* Introduced new interfaces `Marshaler` (method `MarshalETF`) and `Unmarshaler` (method `UnmarshalETF`) for the custom encoding/decoding data.
21+
* Improved performance for the local messaging (up to 3 times for some cases)
22+
* Added example [examples/http](examples/http) to demonsrate how HTTP server can be integrated into the Ergo node.
23+
* 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.
24+
* Added support FreeBSD, OpenBSD, NetBSD, DragonFly.
25+
* Fixed RPC issue #45
26+
* Fixed internal timer issue #48
27+
* Fixed memory leaks #53
28+
* Fixed double panic issue #52
29+
* Fixed Atom Cache race conditioned issue #54
30+
* Fixed ETF encoder issues #64 #66
31+
732
#### [1.2.0](https://github.com/ergo-services/ergo/releases/tag/v1.2.0) - 2021-04-07 ####
833

934
* Added TLS support. Introduced new option `TLSmode` in `ergo.NodeOptions` with the following values:

README.md

+92-60
Large diffs are not rendered by default.

debug.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
//+build debug
1+
//go:build debug
2+
// +build debug
23

34
package ergo
45

ergo.go

+23-2
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,44 @@ import (
66
"github.com/ergo-services/ergo/erlang"
77
"github.com/ergo-services/ergo/gen"
88
"github.com/ergo-services/ergo/node"
9+
"github.com/ergo-services/ergo/proto/dist"
910
)
1011

1112
// StartNode create new node with name and cookie string
1213
func StartNode(name string, cookie string, opts node.Options) (node.Node, error) {
1314
return StartNodeWithContext(context.Background(), name, cookie, opts)
1415
}
1516

16-
// CreateNodeWithContext create new node with specified context, name and cookie string
17+
// StartNodeWithContext create new node with specified context, name and cookie string
1718
func StartNodeWithContext(ctx context.Context, name string, cookie string, opts node.Options) (node.Node, error) {
1819
version := node.Version{
1920
Release: Version,
2021
Prefix: VersionPrefix,
2122
OTP: VersionOTP,
2223
}
24+
if opts.Env == nil {
25+
opts.Env = make(map[gen.EnvKey]interface{})
26+
}
27+
opts.Env[node.EnvKeyVersion] = version
2328

2429
// add erlang support application
2530
opts.Applications = append([]gen.ApplicationBehavior{&erlang.KernelApp{}}, opts.Applications...)
2631

27-
return node.StartWithContext(context.WithValue(ctx, "version", version), name, cookie, opts)
32+
if opts.Handshake == nil {
33+
// create default handshake for the node (Erlang Dist Handshake)
34+
opts.Handshake = dist.CreateHandshake(dist.HandshakeOptions{})
35+
}
36+
37+
if opts.Proto == nil {
38+
// create default proto handler (Erlang Dist Proto)
39+
protoOptions := node.DefaultProtoOptions()
40+
opts.Proto = dist.CreateProto(protoOptions)
41+
}
42+
43+
if opts.StaticRoutesOnly == false && opts.Resolver == nil {
44+
// create default resolver (with enabled Erlang EPMD server)
45+
opts.Resolver = dist.CreateResolverWithLocalEPMD("", dist.DefaultEPMDPort)
46+
}
47+
48+
return node.StartWithContext(ctx, name, cookie, opts)
2849
}

erlang/appmon.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ type jobDetails struct {
2626
}
2727

2828
// Init initializes process state using arbitrary arguments
29-
// Init -> state
3029
func (am *appMon) Init(process *gen.ServerProcess, args ...etf.Term) error {
3130
lib.Log("APP_MON: Init %#v", args)
3231
from := args[0]
@@ -37,10 +36,11 @@ func (am *appMon) Init(process *gen.ServerProcess, args ...etf.Term) error {
3736
return nil
3837
}
3938

39+
// HandleCast
4040
func (am *appMon) HandleCast(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
4141
var appState *appMonState = process.State.(*appMonState)
4242
lib.Log("APP_MON: HandleCast: %#v", message)
43-
node := process.Env("ergo:Node").(node.Node)
43+
node := process.Env(node.EnvKeyNode).(node.Node)
4444
switch message {
4545
case "sendStat":
4646

@@ -137,7 +137,7 @@ func (am *appMon) HandleCast(process *gen.ServerProcess, message etf.Term) gen.S
137137
}
138138

139139
func (am *appMon) makeAppTree(process gen.Process, app etf.Atom) etf.Tuple {
140-
node := process.Env("ergo:Node").(node.Node)
140+
node := process.Env(node.EnvKeyNode).(node.Node)
141141
appInfo, err := node.ApplicationInfo(string(app))
142142
if err != nil {
143143
return nil

erlang/erlang.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ type erlang struct {
1212
gen.Server
1313
}
1414

15+
// Init
1516
func (e *erlang) Init(process *gen.ServerProcess, args ...etf.Term) error {
1617
lib.Log("ERLANG: Init: %#v", args)
1718
return nil
1819
}
1920

21+
// HandleCall
2022
func (e *erlang) HandleCall(process *gen.ServerProcess, from gen.ServerFrom, message etf.Term) (etf.Term, gen.ServerStatus) {
2123
lib.Log("ERLANG: HandleCall: %#v, From: %#v", message, from)
2224

@@ -99,7 +101,7 @@ func processInfo(p gen.Process, pid etf.Pid, property etf.Term) etf.Term {
99101
case etf.Atom("priority"):
100102
// values = append(values, etf.Tuple{p[i], 0})
101103
case etf.Atom("reductions"):
102-
values = append(values, etf.Tuple{p[i], info.Reductions})
104+
values = append(values, etf.Tuple{p[i], 0})
103105
case etf.Atom("registered_name"):
104106
values = append(values, etf.Tuple{p[i], process.Name()})
105107
case etf.Atom("sequential_trace_token"):

erlang/global_name_server.go

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type globalNameServer struct {
1111
gen.Server
1212
}
1313

14+
// HandleCast
1415
func (gns *globalNameServer) HandleCast(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
1516
return gen.ServerStatusOK
1617
}

erlang/net_kernel.go

+14-7
Original file line numberDiff line numberDiff line change
@@ -13,50 +13,54 @@ import (
1313
"github.com/ergo-services/ergo/lib/osdep"
1414
)
1515

16+
// KernelApp
1617
type KernelApp struct {
1718
gen.Application
1819
}
1920

21+
// Load
2022
func (nka *KernelApp) Load(args ...etf.Term) (gen.ApplicationSpec, error) {
2123
return gen.ApplicationSpec{
2224
Name: "erlang",
2325
Description: "Erlang support app",
2426
Version: "v.1.0",
2527
Children: []gen.ApplicationChildSpec{
26-
gen.ApplicationChildSpec{
28+
{
2729
Child: &netKernelSup{},
2830
Name: "net_kernel_sup",
2931
},
3032
},
3133
}, nil
3234
}
3335

36+
// Start
3437
func (nka *KernelApp) Start(p gen.Process, args ...etf.Term) {}
3538

3639
type netKernelSup struct {
3740
gen.Supervisor
3841
}
3942

43+
// Init
4044
func (nks *netKernelSup) Init(args ...etf.Term) (gen.SupervisorSpec, error) {
4145
return gen.SupervisorSpec{
4246
Children: []gen.SupervisorChildSpec{
43-
gen.SupervisorChildSpec{
47+
{
4448
Name: "net_kernel",
4549
Child: &netKernel{},
4650
},
47-
gen.SupervisorChildSpec{
51+
{
4852
Name: "global_name_server",
4953
Child: &globalNameServer{},
5054
},
51-
gen.SupervisorChildSpec{
55+
{
5256
Name: "rex",
5357
Child: &rex{},
5458
},
55-
gen.SupervisorChildSpec{
59+
{
5660
Name: "observer_backend",
5761
Child: &observerBackend{},
5862
},
59-
gen.SupervisorChildSpec{
63+
{
6064
Name: "erlang",
6165
Child: &erlang{},
6266
},
@@ -75,12 +79,14 @@ type netKernel struct {
7579
routinesCtx map[etf.Pid]context.CancelFunc
7680
}
7781

82+
// Init
7883
func (nk *netKernel) Init(process *gen.ServerProcess, args ...etf.Term) error {
7984
lib.Log("NET_KERNEL: Init: %#v", args)
8085
nk.routinesCtx = make(map[etf.Pid]context.CancelFunc)
8186
return nil
8287
}
8388

89+
// HandleCall
8490
func (nk *netKernel) HandleCall(process *gen.ServerProcess, from gen.ServerFrom, message etf.Term) (reply etf.Term, status gen.ServerStatus) {
8591
lib.Log("NET_KERNEL: HandleCall: %#v, From: %#v", message, from)
8692
status = gen.ServerStatusOK
@@ -124,6 +130,7 @@ func (nk *netKernel) HandleCall(process *gen.ServerProcess, from gen.ServerFrom,
124130
return
125131
}
126132

133+
// HandleInfo
127134
func (nk *netKernel) HandleInfo(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
128135
lib.Log("NET_KERNEL: HandleInfo: %#v", message)
129136
switch m := message.(type) {
@@ -147,7 +154,7 @@ func sendProcInfo(p gen.Process, to etf.Pid) {
147154
etf.Atom("etop_proc_info"), // record name #etop_proc_info
148155
list[i].Self(), // pid
149156
0, // mem
150-
info.Reductions, // reds
157+
0, // reds
151158
etf.Atom(list[i].Name()), // etf.Tuple{etf.Atom("ergo"), etf.Atom(list[i].Name()), 0}, // name
152159
0, // runtime
153160
info.CurrentFunction, // etf.Tuple{etf.Atom("ergo"), etf.Atom(info.CurrentFunction), 0}, // cf

erlang/observer_backend.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,13 @@ type observerBackend struct {
1919
}
2020

2121
// Init initializes process state using arbitrary arguments
22-
// Init(...) -> state
2322
func (o *observerBackend) Init(process *gen.ServerProcess, args ...etf.Term) error {
2423
lib.Log("OBSERVER: Init: %#v", args)
2524

2625
funProcLibInitialCall := func(a ...etf.Term) etf.Term {
2726
return etf.Tuple{etf.Atom("proc_lib"), etf.Atom("init_p"), 5}
2827
}
29-
node := process.Env("ergo:Node").(node.Node)
28+
node := process.Env(node.EnvKeyNode).(node.Node)
3029
node.ProvideRPC("proc_lib", "translate_initial_call", funProcLibInitialCall)
3130

3231
funAppmonInfo := func(a ...etf.Term) etf.Term {
@@ -42,6 +41,7 @@ func (o *observerBackend) Init(process *gen.ServerProcess, args ...etf.Term) err
4241
return nil
4342
}
4443

44+
// HandleCall
4545
func (o *observerBackend) HandleCall(state *gen.ServerProcess, from gen.ServerFrom, message etf.Term) (etf.Term, gen.ServerStatus) {
4646
lib.Log("OBSERVER: HandleCall: %v, From: %#v", message, from)
4747
function := message.(etf.Tuple).Element(1).(etf.Atom)
@@ -68,7 +68,7 @@ func (o *observerBackend) HandleCall(state *gen.ServerProcess, from gen.ServerFr
6868

6969
func (o *observerBackend) sysInfo(p gen.Process) etf.List {
7070
// observer_backend:sys_info()
71-
node := p.Env("ergo:Node").(node.Node)
71+
node := p.Env(node.EnvKeyNode).(node.Node)
7272
processCount := etf.Tuple{etf.Atom("process_count"), len(p.ProcessList())}
7373
processLimit := etf.Tuple{etf.Atom("process_limit"), 262144}
7474
atomCount := etf.Tuple{etf.Atom("atom_count"), 0}

erlang/rex.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type rex struct {
2828
methods map[modFun]gen.RPC
2929
}
3030

31+
// Init
3132
func (r *rex) Init(process *gen.ServerProcess, args ...etf.Term) error {
3233
lib.Log("REX: Init: %#v", args)
3334
// Do not overwrite existing methods if this process restarted
@@ -42,11 +43,12 @@ func (r *rex) Init(process *gen.ServerProcess, args ...etf.Term) error {
4243
}
4344
r.methods[mf] = nil
4445
}
45-
node := process.Env("ergo:Node").(node.Node)
46+
node := process.Env(node.EnvKeyNode).(node.Node)
4647
node.ProvideRemoteSpawn("erpc", &erpc{})
4748
return nil
4849
}
4950

51+
// HandleCall
5052
func (r *rex) HandleCall(process *gen.ServerProcess, from gen.ServerFrom, message etf.Term) (etf.Term, gen.ServerStatus) {
5153
lib.Log("REX: HandleCall: %#v, From: %#v", message, from)
5254
switch m := message.(type) {
@@ -63,7 +65,7 @@ func (r *rex) HandleCall(process *gen.ServerProcess, from gen.ServerFrom, messag
6365
return reply, gen.ServerStatusOK
6466
}
6567

66-
to := gen.ProcessID{string(module), process.NodeName()}
68+
to := gen.ProcessID{Name: string(module), Node: process.NodeName()}
6769
m := etf.Tuple{m.Element(3), m.Element(4)}
6870
reply, err := process.Call(to, m)
6971
if err != nil {
@@ -78,11 +80,13 @@ func (r *rex) HandleCall(process *gen.ServerProcess, from gen.ServerFrom, messag
7880
return reply, gen.ServerStatusOK
7981
}
8082

83+
// HandleInfo
8184
func (r *rex) HandleInfo(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
8285
// add this handler to suppres any messages from erlang
8386
return gen.ServerStatusOK
8487
}
8588

89+
// HandleDirect
8690
func (r *rex) HandleDirect(process *gen.ServerProcess, message interface{}) (interface{}, error) {
8791
switch m := message.(type) {
8892
case gen.MessageManageRPC:
@@ -179,6 +183,7 @@ type erpc struct {
179183
gen.Server
180184
}
181185

186+
// Init
182187
func (e *erpc) Init(process *gen.ServerProcess, args ...etf.Term) error {
183188
lib.Log("ERPC [%v]: Init: %#v", process.Self(), args)
184189
mfa := erpcMFA{
@@ -192,6 +197,7 @@ func (e *erpc) Init(process *gen.ServerProcess, args ...etf.Term) error {
192197

193198
}
194199

200+
// HandleCast
195201
func (e *erpc) HandleCast(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
196202
lib.Log("ERPC [%v]: HandleCast: %#v", process.Self(), message)
197203
mfa := message.(erpcMFA)

0 commit comments

Comments
 (0)