Giter VIP home page Giter VIP logo

ergo's People

Contributors

alexkovalevych avatar dmitrivereshchagin avatar ftrvxmtrx avatar halturin avatar juise avatar larrouse2015 avatar mdevilliers avatar mytholog avatar rutaka-n avatar zert avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ergo's Issues

resource is taken

Call message from remote node, error code is received for the first time: Can't connect to [email protected] Resource is taken, continue communication with no problem, not sure whether the initial connection of the node is wrong, or the process connecting to the remote node is wrong? Do you need to ping each other in advance to avoid this problem?

Locking...

I was wondering if node.Send() was safe to call from a worker go routine, so I was digging around in the code and noticed that some structures node.connections, for example, was protected by a mutex for writing, but not for reading. This is not sufficient (unless I'm missing something?)

node.connections could ideally be protected by a RWMutex (https://golang.org/pkg/sync/#RWMutex) that can be held by many readers but only one writer...

Let me know if you are interested in a pull request, or am I missing something clever?

Fantastic project!! After struggling with writing a stdin/stdout port with a lot of buffering issues I'm so happy I found this!

Not protected:
https://github.com/halturin/ergonode/blob/d13e6eebb13d3f1589a62e789ce4a947fc24ed34/ergonode.go#L442

Protected:
https://github.com/halturin/ergonode/blob/c08e4a55356eb58cbe82a07a1d020153c550b003/ergonode.go#L373

Unittest `TestDecodeFragment` occasionally fails

Describe the bug

All unittests run stabil, only TestDecodeFragment occasionally fails.

To Reproduce

image

https://github.com/ergo-services/ergo/actions/runs/3106223372/jobs/5032829002#step:4:242

Expected behavior

Should be success in stable manner.

Screenshots

seq 10 | xargs -Iz go test -count 1 -run ^TestDecodeFragment$ github.com/ergo-services/ergo/proto/dist
--- FAIL: TestDecodeFragment (0.36s)
    proto_test.go:288: got nil result
FAIL
FAIL    github.com/ergo-services/ergo/proto/dist        0.365s
FAIL
ok      github.com/ergo-services/ergo/proto/dist        0.367s
ok      github.com/ergo-services/ergo/proto/dist        0.367s
ok      github.com/ergo-services/ergo/proto/dist        0.369s
ok      github.com/ergo-services/ergo/proto/dist        0.372s
--- FAIL: TestDecodeFragment (0.36s)
    proto_test.go:288: got nil result
FAIL
FAIL    github.com/ergo-services/ergo/proto/dist        0.365s
FAIL
ok      github.com/ergo-services/ergo/proto/dist        0.368s
ok      github.com/ergo-services/ergo/proto/dist        0.368s
--- FAIL: TestDecodeFragment (0.36s)
    proto_test.go:288: got nil result
FAIL
FAIL    github.com/ergo-services/ergo/proto/dist        0.367s
FAIL
--- FAIL: TestDecodeFragment (0.36s)
    proto_test.go:288: got nil result
FAIL
FAIL    github.com/ergo-services/ergo/proto/dist        0.366s
FAIL

Environment (please complete the following information):

	Operating System
	Ubuntu
	20.0.5
	LTS
  
  go env
	GO111MODULE=""
	GOARCH="amd64"
	GOBIN=""
	GOCACHE="/home/runner/.cache/go-build"
	GOENV="/home/runner/.config/go/env"
	GOEXE=""
	GOEXPERIMENT=""
	GOFLAGS=""
	GOHOSTARCH="amd64"
	GOHOSTOS="linux"
	GOINSECURE=""
	GOMODCACHE="/home/runner/go/pkg/mod"
	GONOPROXY=""
	GONOSUMDB=""
	GOOS="linux"
	GOPATH="/home/runner/go"
	GOPRIVATE=""
	GOPROXY="https://proxy.golang.org,direct"
	GOROOT="/opt/hostedtoolcache/go/1.17.13/x64"
	GOSUMDB="sum.golang.org"
	GOTMPDIR=""
	GOTOOLDIR="/opt/hostedtoolcache/go/1.17.13/x64/pkg/tool/linux_amd64"
	GOVCS=""
	GOVERSION="go1.17.13"
	GCCGO="gccgo"
	AR="ar"
	CC="gcc"
	CXX="g++"
	CGO_ENABLED="1"
	GOMOD="/home/runner/work/ergo/ergo/go.mod"
	CGO_CFLAGS="-g -O2"
	CGO_CPPFLAGS=""
	CGO_CXXFLAGS="-g -O2"
	CGO_FFLAGS="-g -O2"
	CGO_LDFLAGS="-g -O2"
	PKG_CONFIG="pkg-config"
	GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build1316604600=/tmp/go-build -gno-record-gcc-switches"

RPC call 'active_tasks' returns a wrong result

Hello.

I'm using RPC calls from go to erlang node (I'm writing an erlang prometheus exporter) and I do the following RPC call:
On erlang node:

(demo@MBP-Alexandr)12> erlang:statistics(active_tasks).
[0,0,0,1,0,0,0,0,0,0,0,0,0]

As we can see it is the list.

So now I expect the same result when I do RPC call from golang to this erlang node:

activeTasks, err = process.CallRPCWithTimeout(8, node, "erlang", "statistics", etf.Atom("active_tasks"))

But I receive the following result:
image

Could you say there is a workaround for this? I tested and found that almost all lists are returned in the same way.

I doubt benchmarks be fair !!!

https://erlangforums.com/t/ring-benchmark-erlang-vs-go/684/12

in erlang community we did some benchkmarks around (raw golang) and ( erlang )
erlang was winner in message passing.
erlang internally used lock-free algorithm for registry and communication between mailbox.
but golang used mutex for channels !!! and in your framework heavily using mutex for synchronization !!!

how is ergo with overhead of framework can beats erlang ??!!!

i found rpc:call from erlang to ergo is not working

Discussed in #114

Originally posted by xiaodoudou12 November 18, 2022
my OTP version is Erlang/OTP 24 [erts-12.1.5] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit].
golang version is go1.18.ergo version is v1.999.211.
i use https://github.com/ergo-services/ergo/blob/v1.999.211/examples/genserver/server.go this to test.
gen_server:cast and gen_server:call is ok,but rpc:call failed.
https://github.com/ergo-services/ergo/blob/v1.999.211/proto/dist/proto.go in this file

			case distProtoSPAWN_REQUEST:
				// {29, ReqId, From, GroupLeader, {Module, Function, Arity}, OptList}
				lib.Log("[%s] CONTROL SPAWN_REQUEST [from %s]: %#v", dc.nodename, dc.peername, message.control)
				if message.proxy != nil && message.proxy.session.NodeFlags.EnableRemoteSpawn == false {
					// we didn't allow this feature. proxy session will be closed due to
					// this violation of the contract
					return node.ErrPeerUnsupported
				}
				registerName := ""
				for _, option := range t.Element(6).(etf.List) {
					name, ok := option.(etf.Tuple)
					if !ok || len(name) != 2 {
						return fmt.Errorf("malformed spawn request")
					}
					switch name.Element(1) {
					case etf.Atom("name"):
						registerName = string(name.Element(2).(etf.Atom))
					}
				}

				from := t.Element(3).(etf.Pid)
				ref := t.Element(2).(etf.Ref)

				mfa := t.Element(5).(etf.Tuple)
				module := mfa.Element(1).(etf.Atom)
				function := mfa.Element(2).(etf.Atom)
				var args etf.List
				if str, ok := message.payload.(string); !ok {
					args, _ = message.payload.(etf.List)
				} else {
					// stupid Erlang's strings :). [1,2,3,4,5] sends as a string.
					// args can't be anything but etf.List.
					for i := range []byte(str) {
						args = append(args, str[i])
					}
				}

the message value is

&dist.distMessage{
control:etf.Tuple{29, etf.Ref{Node:"[email protected]", Creation:0x636ca74d, ID:[5]uint32{0x143cd, 0x843c0001, 0x9a32d1ab, 0x0, 0x0}}, etf.Pid{Node:"[email protected]", ID:0x55, Creation:0x636ca74d}, etf.Pid{Node:"[email protected]", ID:0x47, Creation:0x636ca74d}, etf.Tuple{"erpc", "execute_call", 4}, etf.List{"monitor"}}, payload:etf.List{etf.Ref{Node:"[email protected]", Creation:0x636ca74d, ID:[5]uint32{0x143cb, 0x843c0001, 0x9a32d1ab, 0x0, 0x0}}, "rpc", "request", etf.List{"hello", 3.14}}, proxy:(*dist.proxySession)(nil)}

t.Element(6). is etf.List{"monitor"}, and len(name) != 2, so it failed
i found registerName should be erpc, i don't know is conrrect.

and node/core.go in function func (c *core) RouteSpawnRequest

		// spawn new process
		process_opts := processOptions{}
		process_opts.Env = map[gen.EnvKey]interface{}{EnvKeyRemoteSpawn: request.Options}
		process, err_spawn := c.spawn(request.Options.Name, process_opts, b.Behavior, args...)

process_opts.Env show init add "ergo:RemoteSpawnRequest" like this

process_opts.Env = map[gen.EnvKey]interface{}{EnvKeyRemoteSpawn: request.Options, "ergo:RemoteSpawnRequest":request}

because in erlang/rex.go in func (e *erpc) HandleCast will need process.Env("ergo:RemoteSpawnRequest")

func (e *erpc) HandleCast(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
	lib.Log("ERPC [%v]: HandleCast: %#v", process.Self(), message)
	mfa := message.(erpcMFA)
	rsr := process.Env("ergo:RemoteSpawnRequest").(gen.RemoteSpawnRequest)

so that,rpc:call from erlang to ergo work ok,but rpc:cast not work also.

in erlang i use like this
([email protected])1> rpc:call('[email protected]', rpc, request, [hello, 3.14]).
[hello,3.14]

Erlang 23 support

Hello!

Do you have a plan to add support for Erlang 23?

Here is an error from Erlang 23 node when I try to call RPC on it from go:

➜ ~ erl -sname demo -setcookie 123
Erlang/OTP 23 [erts-11.2.1] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [hipe] [dtrace]

Eshell V11.2.1  (abort with ^G)
(demo@MBP-Alexandr)1> =ERROR REPORT==== 11-May-2021::09:24:34.078882 ===
** 'demo@MBP-Alexandr': Connection attempt from node 'test@MBP-Alexandr' rejected since it cannot handle ["BIG_CREATION"].**

how to use simple_one_for_one under supervisor tree

Hi:
I'm writing something which use supervisor:start_child to dynamic create worker process.
But my code show's me these processes share same struct which I just make for gen.Server behavior.
Maybe I should write some creater code with method to create the behavior struct , but I donnot know how to do this with supervisor.
Is there some demo code show me how to do this ?
Or I just can do this with spawn process which never under supervisor tree ?

demo run error

$ pwd
/Users/shiqi/go/src/github.com/ergo/examples/genserver

$ go run demoGenServer.go
Init: args [] 
Run erl shell:
erl -name [email protected] -setcookie 123
-----Examples that can be tried from 'erl'-shell
gen_server:cast({example,'[email protected]'}, stop).
gen_server:call({example,'[email protected]'}, hello).

when i run erl shell

$ erl -name [email protected] -setcookie 123
Erlang/OTP 23 [erts-11.1.3] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [hipe] [dtrace]

Eshell V11.1.3  (abort with ^G)
([email protected])1> gen_server:cast({example,'[email protected]'}, stop).
ok
([email protected])2> =ERROR REPORT==== 21-May-2021::17:45:03.682489 ===
** '[email protected]': Connection attempt to node '[email protected]' aborted since it cannot handle ["BIG_CREATION"].**

gen_server:call({example,'[email protected]'}, hello).
=ERROR REPORT==== 21-May-2021::17:45:12.825623 ===
** '[email protected]': Connection attempt to node '[email protected]' aborted since it cannot handle ["BIG_CREATION"].**

** exception exit: {{nodedown,'[email protected]'},
                    {gen_server,call,[{example,'[email protected]'},hello]}}
     in function  gen_server:call/2 (gen_server.erl, line 238)
([email protected])3> 

go version 1.15.1

Remove use of `syscall` package

When trying to integrate ergo in my application, I bumped into the following compilation problem on my Mac:

# github.com/halturin/ergo
../../golang/pkg/mod/github.com/halturin/ergo@v1.1.0/node.go:759:63: undefined: syscall.TCP_KEEPINTVL
make: *** [build-controller] Error 2

When checking the docs, I noticed that this constant is indeed only available for GOOS=linux and not GOOS=darwin:

Nevertheless, this package contains a deprecation message:

Deprecated: this package is locked down. Callers should use the corresponding package in the golang.org/x/sys repository instead. That is also where updates required by new systems or versions should be applied. See https://golang.org/s/go1.4-syscall for more information.

So I guess the use of syscall should be removed and replaced by package golang.org/x/sys. For the constant at hand, there is the constant SO_KEEPALIVE in both the unix and windows packages:

https://pkg.go.dev/golang.org/x/[email protected]/unix?tab=doc#pkg-constants
https://pkg.go.dev/golang.org/x/[email protected]/windows?tab=doc#pkg-constants

Could you update ergo with this change and make a new release?

Platform specific build tags is probably what you need here:
https://dave.cheney.net/2013/10/12/how-to-use-conditional-compilation-with-the-go-build-tool

How to wrap a blocking server in a GenServer?

In the Erlang/Elixir world, everybody models their processes the OTP way. But not in the Go world. A lot of them are modelled as goroutines. So here is an integration question between those two worlds.

I have a Kubernetes controller, implemented via operator-sdk which I want to wrap in an ergo process. My Kubernetes controller main has currently this as the last lines of code:

	setupLog.Info("starting manager")
	stopCh := ctrl.SetupSignalHandler()
	if err := mgr.Start(stopCh); err != nil {
		setupLog.Error(err, "problem running manager")
		os.Exit(1)
	}

The Start function is blocking, unless if there is an error.

When I move it to a GenServer setup, I will add the mgr to my process state, and I can execute mgr.Start from a goroutine within the Init function. To get to know about a possible err result, I would pass an errCh to the goroutine.

Question remains how to integrate the lifecycle between my Genserver and mgr.

a. ergo node stops and calls Terminate on all processes: I close the stopCh (part of the state) and the controller stops as a result. I guess all fine here.
b. the mgr errors at start and this err is sent back via the errCh I mentioned. At what point should I check the errCh? To have a non-blocking check, I would at least use select on the errCh during Init, but due to timing, the message could come later. How can I "schedule" another check a bit later?

Failed to connect from elixir 1.7.4, otp 21

iex(c@alex-pc)1> :net_adm.ping(:'b@alex-pc')    

03:23:46.922 [error] ** :"c@alex-pc": Connection attempt to node :"b@alex-pc" aborted since it cannot handle ['NEW_FUN_TAGS'].**

:pang

Panic when sending time.Time

Somewhere in this library will panic when sending a golang time.Time:

panic: reflect.Value.Interface: cannot return value obtained from unexported field or method

The golang reflect documentation suggests to use reflect.Value.CanInterface() before calling reflect.Value.Interface(). If !v.CanInterface(), we could use reflect.Value.String() instead or the unexported access technique in the below link.

Any example show how to send a message to erlang node?

erlang code:

-module(kvs).                                 
-export([start/0,store/2,lookup/1]).          
start()->                                     
    register(kvs,spawn(fun() -> loop() end)). 
                                              
store(Key,Value)->                            
    io:format("xxx\n"),                       
    rpc({store,Key,Value}).                   
                                              
lookup(Key)->                                 
    rpc({lookup,Key}).                        
                                              
rpc(Q)->                                      
    kvs ! {self(), Q},                        
    receive                                   
        {kvs, Reply} ->                       
            Reply                             
    end.                                      
                                              
loop() ->                                     
    receive                                   
        {From, {store, Key, Value}} ->        
            put(Key, {ok, Value}),            
            From ! {kvs, true},               
            loop();                           
        {From, {lookup, Key}} ->              
            From ! {kvs, get(Key)},           
            loop()                            
    end.                                      

I use simple/GenServer.go

process.Call(etf.Tuple{"kvs", "erlmain@localhost"}, etf.Term(etf.List{"weather", "fine"}))

can not recv the result from erlang node erlmain@localhost

Ergonode stuck after first message received

Having ergonode running in kubernetes cluster as docker container from image:

FROM golang:1.11.5-alpine3.9 as builder

ARG APP_NAME

WORKDIR /${GOPATH}/src/${APP_NAME}

ADD . .

RUN apk add git curl

RUN curl -fsSL -o /usr/local/bin/dep https://github.com/golang/dep/releases/download/v0.5.0/dep-linux-amd64 && chmod +x /usr/local/bin/dep

RUN dep ensure -vendor-only

RUN go build -o /src/${APP_NAME}_build main.go

FROM erlang:21.2.5-alpine

RUN apk update && apk add --no-cache \
  ncurses-libs \
  zlib \
  ca-certificates \
  openssl \
  bash

RUN rm -rf /var/cache/apk/*

WORKDIR /root

ARG APP_NAME

COPY --from=builder /src/${APP_NAME}_build .

CMD ["./${APP_NAME}_build"]

Successfully connected to it from an elixir node, but after the first message sent, it's stuck:

iex([email protected])34> GenServer.call({:mongo_transaction, :'[email protected]'}, :pid)
#PID<35993.4.1>
iex([email protected])35> GenServer.call({:mongo_transaction, :'[email protected]'}, :pid)
** (exit) exited in: GenServer.call({:mongo_transaction, :"[email protected]"}, :pid, 5000)
    ** (EXIT) time out
    (elixir) lib/gen_server.ex:989: GenServer.call/3

After some time (about minute or something) golang node responses again, but again stuck after the first message.

So, i guess the issue is reproducible if elixir node and golang node have different epmd servers (since each is running in a separate kubernetes pod), locally everything works fine.

How to use ergo epmd instead of standard Erlang epmd?

Hi, first of all I would like to congratulate you for the excellent project.

I would like to know if and how it would be possible to run the epmd you provide instead of the standard Erlang epmd.
I would also like to know if there is any benchmark of you just doing the drop-in replacement of epmd using applications written purely in Erlang/Elixir.

Can you help me ?

GenStage available?

Hello, you don't have a GenStage implementation available for the ergo project? This would be a valuable addition.

The message from the remote peer silently drops if the mailbox is full.

There was an issue #95 I've moved to the discussion https://github.com/ergo-services/ergo/discussions/95 by mistake (misunderstood the real problem).

There should be a way to handle the situation of overflowing the mailbox during the message delivery from the remote peer. Current implementation just silently drops the message (master branch: https://github.com/ergo-services/ergo/blob/master/node/network.go#L346 and in the current development branch https://github.com/ergo-services/ergo/blob/v210/proto/dist/proto.go#L760)

Thanks to @jiait for pointing to this issue.

Second CallRPC method is stuck when it is called against remote erlang node

Describe the bug
I can't do CallRPC to remote erlang node more than one time.
The second CallRPC is stuck due to this line in the code:

sp.callbackWaitReply <- &ref

To Reproduce
Steps to reproduce the behavior:

Probably, I'm doing something wrong, but there is no documentation for this case.

My code:

func CreateNewNode() *ErlangHelper {
	eh := new(ErlangHelper)
	opts := node2.Options{}

	// Initialize new node with given name, cookie, listening port range and epmd port
	node, err := ergo.StartNode(configuration.Cfg.SelfNodeFullName, configuration.Cfg.Cookie, opts)
	if err != nil {
		panic(err)
	}
	// Initialize new instance of demoGenServ structure which implements Process behaviour
	_, _ = node.Spawn("exporter", gen.ProcessOptions{}, eh)
	return eh
}

type ErlangHelper struct {
	gen.Server
	ErlangProcess
}

type ErlangProcess struct {
	*gen.ServerProcess
}

func (eh *ErlangHelper) Init(process *gen.ServerProcess, args ...etf.Term) error {
	demo := &ErlangProcess{
		ServerProcess: process,
	}
	// do not inherit parent State
	demo.State = nil

	//if err := process.Behavior().(DemoBehavior).InitDemo(demo, args...); err != nil {
	//	return err
	//}
	process.State = demo
	eh.ServerProcess = process
	return nil
}

func (eh *ErlangHelper) HandleInfo(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
	return gen.ServerStatusOK
}

func (eh *ErlangHelper) CallRPC(node string, module string, f string, args ...etf.Term) etf.Term {
	log.Debugf("Calling RPC to %v, module %v, func %v, args %v", node, module, f, args)
	res, err := eh.ServerProcess.CallRPCWithTimeout(1, node, module, f, args...)
	if err != nil {
		panic(err)
	}
	return res
}

When I do CallRPC first time, it works, I receive information from an erlang (23) node.

But the second one is stuck, because no one reads sp.callbackWaitReply channel in the second call (I don't know why).

The only one thins that I need is send RPCs to another erlang nodes.

What I've missed? Thanks.

Expected behavior
RPC calls work

Screenshots

Environment (please complete the following information):

  • Arch: aarch64
  • OS: Mac
  • Framework Version 2.2.0
  • 10

connect to erlang 20 failed

erl -name [email protected] --setcookie 123
Erlang/OTP 20 [erts-9.1.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Eshell V9.1.2  (abort with ^G)
([email protected])2> net_adm:ping('[email protected]').
pang
([email protected])3>
=ERROR REPORT==== 14-Oct-2017::21:45:42 ===
** '[email protected]': Connection attempt to node '[email protected]' aborted since it cannot handle ["UTF8_ATOMS"].**

([email protected])3> erlang:system_info(otp_release).
"20"

Elixir GenStage crashes when receiving demand from Ergo

Describe the bug
Elixir GenStage crashes when receiving demand from Ergo.
Error:

[error] GenServer Producer terminating
** (FunctionClauseError) no function clause matching in GenStage.maybe_producer_cancel/2

To Reproduce
I made a small repo with the reproduction steps:
https://github.com/okkdev/ergo_elixir_genstage_issue

Expected behavior
Have the Elixir Producer handle the demand and Ergo do the work.

Environment (please complete the following information):

  • Arch: arm64
  • OS: macOS
  • Framework Version v1.999.210
  • Number of CPU: 10

Help create process ambiguity,or bug!

When supvisor strategy Type is gen SupervisorStrategySimpleOneForOne, use sup.StartChild API, created processes are shared a child object, Theoretically, a separate process requires a separate child object; Don't know that the design is intentional; I need a different process with a different object that can only be created using the node.spawn interface, not supvisor; When the type is SupervisorStrategySimpleOneForOne, whether to help special processing, separate process requires a separate object entities

RPC call sometimes returns the error 'timeout' instantly

Hello again :)

I've found an unexpected behavior when I run RPC Calls from golang to an erlang node.

Please help me to understand why it happens. Thanks.

You can find the log from go below.
As you may see sometimes for some RPC call command the timeout error is returned instantly after calling RPC for no reason (I have 8 seconds timeout)

INFO[0018] Erlang collector started fetching metrics    
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
ERRO[0018] Error: timeout, Result: <nil>                
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fe1, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fe1, 0x59e9, 0x0}}, etf.Tuple{247329, 4241}}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fe2, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fe2, 0x59e9, 0x0}}, etf.Tuple{247487, 158}}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fe3, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fe3, 0x59e9, 0x0}}, etf.Tuple{247645, 158}}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fe4, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fe4, 0x59e9, 0x0}}, etf.Tuple{247803, 158}}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fe5, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fe5, 0x59e9, 0x0}}, etf.Tuple{247961, 158}}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fe6, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fe6, 0x59e9, 0x0}}, etf.Tuple{2365, 0}}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
ERRO[0018] Error: timeout, Result: <nil>                
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fe7, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fe7, 0x59e9, 0x0}}, etf.Tuple{2368, 0}}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fe8, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fe8, 0x59e9, 0x0}}, etf.Tuple{2371, 0}}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fe9, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fe9, 0x59e9, 0x0}}, etf.Tuple{2374, 0}}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fea, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fea, 0x59e9, 0x0}}, etf.Tuple{2377, 0}}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5feb, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5feb, 0x59e9, 0x0}}, 1}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fec, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fec, 0x59e9, 0x0}}, 1}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fed, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fed, 0x59e9, 0x0}}, 1}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fee, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fee, 0x59e9, 0x0}}, 1}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fef, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5fef, 0x59e9, 0x0}}, 1}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff0, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff0, 0x59e9, 0x0}}, 1}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff1, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff1, 0x59e9, 0x0}}, 1}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff2, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff2, 0x59e9, 0x0}}, 1}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff3, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff3, 0x59e9, 0x0}}, 1}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff4, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff4, 0x59e9, 0x0}}, 1}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff5, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff5, 0x59e9, 0x0}}, 0}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff6, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff6, 0x59e9, 0x0}}, 0}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff7, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff7, 0x59e9, 0x0}}, 0}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff8, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff8, 0x59e9, 0x0}}, 0}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff9, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ff9, 0x59e9, 0x0}}, 0}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ffa, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ffa, 0x59e9, 0x0}}, 0}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ffb, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ffb, 0x59e9, 0x0}}, 0}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ffc, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ffc, 0x59e9, 0x0}}, 0}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ffd, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ffd, 0x59e9, 0x0}}, 0}
2021/04/29 19:59:35 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:statistics
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/29 19:59:35 Node control: etf.Tuple{2, "", etf.Pid{Node:"test@MBP-Alexandr", ID:0x3ef, Serial:0x1, Creation:0x1}}
2021/04/29 19:59:35 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1007 1 1}
2021/04/29 19:59:35 [test@MBP-Alexandr]. {test@MBP-Alexandr 1007 1 1} got message from etf.Pid{Node:"", ID:0x0, Serial:0x0, Creation:0x0}
2021/04/29 19:59:35 got reply: etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ffe, 0x59e9, 0x0}}
etf.Tuple{etf.Ref{Node:"test@MBP-Alexandr", Creation:0x1, ID:[]uint32{0x5ffe, 0x59e9, 0x0}}, 0}
INFO[0018] Erlang collector fetched metrics   

I've created a func to retry failed calls but it's just a workaround:

func (rpc RPC) runCommand(node string, module string, f string, args ...etf.Term) etf.Term {
	var res etf.Term
	var err error
	for i := 0; i < 5; i++ {
		if args == nil {
			res, err = rpc.process.CallRPCWithTimeout(8, node, module, f)
		} else {
			if len(args) == 1 {
				res, err = rpc.process.CallRPCWithTimeout(8, node, module, f, args[0])
			} else {
				res, err = rpc.process.CallRPCWithTimeout(8, node, module, f, args)
			}
		}
		if err != nil {
			log.Errorf("Error: %v, Result: %v\n", err, res)
			continue
		}
	}
	return res
}

Using Struct Tags to Control Encoding

How can we convert go native types into etf.Term via reflection? Is a reflection api included?

E.g. slices of map[string]interface{} to etf.Term?

Observer doesn't connect to demo application

Hello,
I was just testing ergo framework and tried Observebility from erlang tools but it failed.

The followings are the environment information about Erlang and OTP version.
I am not a seasoned Erlang programmer but I think the version should be okay.

Any Idea what am I doing wrong?

image

Race condition caused segfault

Here is details:

[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x5639bf]

goroutine 39 [running]:
example/vendor/github.com/halturin/ergonode/dist.(*NodeDesc).GetRemoteName(...)
    /home/taras/devel/demo/example/vendor/github.com/halturin/ergonode/dist/dist.go:313
example/vendor/github.com/halturin/ergonode.(*Node).run.func2(0xc000104660, 0x5ee6e0, 0xc000114040, 0xc0000ce000, 0xc000104600)
    /home/taras/devel/demo/example/vendor/github.com/halturin/ergonode/ergonode.go:277 +0x17f
created by example/vendor/github.com/halturin/ergonode.(*Node).run
    /home/taras/devel/demo/example/vendor/github.com/halturin/ergonode/ergonode.go:266 +0x10c

these two goroutines in race condition after the link has been closed.

image

getting remote name has no checking for the 'nil' value

func (nd *NodeDesc) GetRemoteName() etf.Atom {
	return etf.Atom(nd.remote.Name)
}

Issues while running the README example

Hi,

I tried to run the README example, but I had some issues.

First, the error was:

# github.com/halturin/ergo
vendor/github.com/halturin/ergo/node.go:759:63: undefined: syscall.TCP_KEEPINTVL

Then, I just changed the syscall.TCP_KEEPINTVL to syscall.TCP_KEEPALIVE, and the new error was:

Warning: recovered process: {node@localhost 1003 1 1} &runtime.TypeAssertionError{_interface:(*runtime._type)(nil), concrete:(*runtime._type)(0x11853a0), asserted:(*runtime._type)(0x117a240), missingMethod:"Init"}
exited

I noticed that the last release was a few months ago and there was a few changes on branch master since that. Then, I just cloned the repo, but I keep having the following errors:

Warning: recovered process(name: gs1){node@localhost 1005 1 1} &runtime.TypeAssertionError{_interface:(*runtime._type)(nil), concrete:(*runtime._type)(0x1287620), asserted:(*runtime._type)(0x12786c0), missingMethod:"Init"}
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x40 pc=0x1241522]

goroutine 1 [running]:
github.com/halturin/ergo.(*Process).Self(...)
	.../github.com/Ocelani/playground/vendor/github.com/halturin/ergo/process.go:92
main.main()
	.../github.com/Ocelani/playground/main.go:68 +0x182
exit status 2

Without the github.com/Ocelani/playground/main.go:68 source line

Warning: recovered process(name: gs1){node@localhost 1003 1 1} &runtime.TypeAssertionError{_interface:(*runtime._type)(nil), concrete:(*runtime._type)(0x12874c0), asserted:(*runtime._type)(0x1278560), missingMethod:"Init"}
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x68 pc=0x124151a]

goroutine 1 [running]:
main.main()
	.../github.com/Ocelani/playground/main.go:71 +0x17a
exit status 2

I could send a pull request solution, but I didn't find any yet, just arrived here.
Although, maybe I am missing something, any pre requisite or something like that.

If there is any solution, could you help me?

Thanks!

Message dropped

Currently, it is found that multiple processes send a large number of messages to the same processing process, and the number of messages received by the processing process is discarded. Please kindly ask whether there is a problem with the capacity of mailbox, or we can learn RabbitMQ gen_server2 to handle it!

GitHub actions fail on all platforms due to integration E2E tests

Describe the bug

GitHub actions always fail by E2E tests on all platforms but without any details.

To Reproduce

Always fail on CI/CD - https://github.com/ergo-services/ergo/actions

Expected behavior

Should be success, not fail.

Screenshots

https://github.com/ergo-services/ergo/actions/runs/3271054409/jobs/5380380573#step:4:1242

...

=== Test TCP Server
Starting nodes: nodeTCP1@localhost: OK
...starting process (gen.TCP): OK
...makeing a new connection: OK
...send/recv data (10 bytes as 1 logic dataframe): OK
...send/recv data (7 bytes as a part of logic dataframe): OK
...send/recv data (5 bytes, must be 1 logic dataframe + extra 2 bytes): OK
...send/recv data (8 bytes, must be 1 logic dataframe): OK
...stopping process (gen.TCP): OK
--- PASS: TestTCP (0.00s)
=== RUN   TestUDP

=== Test UDP Server
Starting nodes: nodeUDP1@localhost: OK
...starting process (gen.UDP): OK
...send/receive data: OK
...stopping process (gen.UDP): OK
--- PASS: TestUDP (0.00s)
=== RUN   TestWeb

=== Test Web Server
Starting nodes: nodeWeb1@localhost: OK
...starting process (gen.Web): OK
...making simple GET request: OK
--- PASS: TestWeb (0.00s)
FAIL
FAIL	github.com/ergo-services/ergo/tests	61.923s
FAIL
Error: Process completed with exit code 1.

Environment (please complete the following information):

Fail on all platforms, below env for Ubuntu:

Operating System
  Ubuntu
  20.0.5
  LTS

go env
  GO111MODULE=""
  GOARCH="amd64"
  GOBIN=""
  GOCACHE="/home/runner/.cache/go-build"
  GOENV="/home/runner/.config/go/env"
  GOEXE=""
  GOEXPERIMENT=""
  GOFLAGS=""
  GOHOSTARCH="amd64"
  GOHOSTOS="linux"
  GOINSECURE=""
  GOMODCACHE="/home/runner/go/pkg/mod"
  GONOPROXY=""
  GONOSUMDB=""
  GOOS="linux"
  GOPATH="/home/runner/go"
  GOPRIVATE=""
  GOPROXY="https://proxy.golang.org,direct"
  GOROOT="/opt/hostedtoolcache/go/1.17.13/x64"
  GOSUMDB="sum.golang.org"
  GOTMPDIR=""
  GOTOOLDIR="/opt/hostedtoolcache/go/1.17.13/x64/pkg/tool/linux_amd64"
  GOVCS=""
  GOVERSION="go1.17.13"
  GCCGO="gccgo"
  AR="ar"
  CC="gcc"
  CXX="g++"
  CGO_ENABLED="1"
  GOMOD="/home/runner/work/ergo/ergo/go.mod"
  CGO_CFLAGS="-g -O2"
  CGO_CPPFLAGS=""
  CGO_CXXFLAGS="-g -O2"
  CGO_FFLAGS="-g -O2"
  CGO_LDFLAGS="-g -O2"
  PKG_CONFIG="pkg-config"
  GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build1316604600=/tmp/go-build -gno-record-gcc-switches"

Additional context

I rerun it using gotestsum command and found out that each time the reason was different, for example, several variants:

https://github.com/dulanov/ergo/actions/runs/3315358118/jobs/5475849237#step:5:1

✖  tests (1m11.876s)

=== Failed
=== FAIL: tests TestNodeProxyConnect (3.29s)

=== Test Node Proxy
... connect NodeA to NodeC via NodeB: OK
... disconnect NodeC from NodeA: OK
... connect NodeA to NodeC via NodeB(transit proxy disabled): OK
... connect NodeA to NodeC (proxy feature support disabled) via NodeB: OK
... connect NodeA to NodeC (with wrong cookie) via NodeB: OK
... connect NodeA to NodeC (with correct cookie) via NodeB: OK
... connect NodeA to NodeD (with enabled encryption) via NodeB: OK
... start processA on NodeA: OK
... start processC on NodeC: OK
... start processD on NodeD: OK
... processA send short message to processC: OK
... processA send short message to processD (encrypted): OK
... processA send 10K message to processC (compressed): OK
... processA send 10K message to processD (compressed, encrypted): OK
... processA send 100K message to processC (fragmented):     server_test.go:897: result timeout

=== FAIL: tests TestServerMessageFlood (2.00s)

=== Test Server message flood 
Starting node: nodeGS1MessageFlood@localhost: OK
    wait for start of gs1source on "nodeGS1MessageFlood@localhost": OK
    wait for start of gs2source on "nodeGS1MessageFlood@localhost": OK
    wait for start of gs3source on "nodeGS1MessageFlood@localhost": OK
    wait for start of gs4source on "nodeGS1MessageFlood@localhost": OK
    wait for start of gs5source on "nodeGS1MessageFlood@localhost": OK
    wait for start of gsdest on "nodeGS1MessageFlood@localhost": OK
2022/10/24 18:57:19 WARNING! Server terminated <C58580E6.0.1014>["gs4source"]. Panic reason: "err on making a call: timed out" at github.com/ergo-services/ergo/tests.(*messageFloodSourceGS).HandleInfo[/home/runner/work/ergo/ergo/tests/server_test.go:675]
    server_test.go:897: result timeout

DONE 64 tests, 2 failures in 82.719s
Error: Process completed with exit code 1.

or https://github.com/dulanov/ergo/actions/runs/3315381867/jobs/5475904571#step:5:1

✖  tests (24.292s)

=== Failed
=== FAIL: tests TestNodeResolveExtra (unknown)

=== Test Node Resolve Extra 
... starting node1 with disabled TLS: OK
... starting node2 with enabled TLS: OK
... node1 resolves node2 with enabled TLS: OK
... node2 resolves node1 with disabled TLS: OK
... node1 connect to node2: OK
... disconnecting nodes: OK
... node2 connect to node1: --- FAIL: TestNodeResolveExtra (0.34s)
panic: runtime error: index out of range [0] with length 0 [recovered]
	panic: runtime error: index out of range [0] with length 0

goroutine 8317 [running]:
testing.tRunner.func1.2({0x14d8280, 0xc000152138})
	/Users/runner/hostedtoolcache/go/1.18.7/x64/src/testing/testing.go:1389 +0x24e
testing.tRunner.func1()
	/Users/runner/hostedtoolcache/go/1.18.7/x64/src/testing/testing.go:1392 +0x39f
panic({0x14d8280, 0xc000152138})
	/Users/runner/hostedtoolcache/go/1.18.7/x64/src/runtime/panic.go:838 +0x207
github.com/ergo-services/ergo/tests.TestNodeResolveExtra(0xc0010a3380)
	/Users/runner/work/ergo/ergo/tests/node_test.go:574 +0xefa
testing.tRunner(0xc0010a3380, 0x1547ba8)
	/Users/runner/hostedtoolcache/go/1.18.7/x64/src/testing/testing.go:1439 +0x102
created by testing.(*T).Run
	/Users/runner/hostedtoolcache/go/1.18.7/x64/src/testing/testing.go:1486 +0x35f

DONE 33 tests, 1 failure in 36.602s
Error: Process completed with exit code 1.

or https://github.com/dulanov/ergo/actions/runs/3315396040/jobs/5475936433#step:5:1

✖  tests (57.031s)

=== Failed
=== FAIL: tests TestNodeProxyConnect (3.33s)

=== Test Node Proxy
... connect NodeA to NodeC via NodeB: OK
... disconnect NodeC from NodeA: OK
... connect NodeA to NodeC via NodeB(transit proxy disabled): OK
... connect NodeA to NodeC (proxy feature support disabled) via NodeB: OK
... connect NodeA to NodeC (with wrong cookie) via NodeB: OK
... connect NodeA to NodeC (with correct cookie) via NodeB: OK
... connect NodeA to NodeD (with enabled encryption) via NodeB: OK
... start processA on NodeA: OK
... start processC on NodeC: OK
... start processD on NodeD: OK
... processA send short message to processC: OK
... processA send short message to processD (encrypted): OK
... processA send 10K message to processC (compressed): OK
... processA send 10K message to processD (compressed, encrypted): OK
... processA send 100K message to processC (fragmented):     server_test.go:897: result timeout

=== FAIL: tests TestRaftGet (0.09s)

=== Test GenRaft - get data
    started raft process <20790EB2.0.1011> with serial 8
    started raft process <B7995A78.0.1011> with serial 5
    started raft process <A4BCA225.0.1011> with serial 6
    started raft process <335CF6EF.0.1011> with serial 3
    started raft process <82F7529F.0.1011> with serial 1
    started raft process <15170655.0.1011> with serial 2
    raft process <20790EB2.0.1011> has already latest serial. skip it
    get serials (6...8) on <B7995A78.0.1011> to reach the leader's serial:     raft_data_test.go:124: no peers with requested serial

=== FAIL: tests TestRaftLeader (0.21s)

=== Test GenRaft - build quorum, leader election
    cluster with  2 distributed raft processes. no quorum, no leader: OK
    cluster with  3 distributed raft processes. quorum of  3 members with 1 leader: OK
    cluster with  4 distributed raft processes. quorum of  3 members with 1 leader + 1 follower(s):     raft_test.go:246: wrong leader serial

DONE 64 tests, 3 failures in 66.270s
Error: Process completed with exit code 1.

freeze serving link between nodes for some period of time in corner case (under huge highload)

making sync call request via Procecc.Call can lock the serving link with the node we calling to.

the problem is in the way of handling incoming messages

p.mailBox <- etf.Tuple{from, message}

if some process (GenServer) is making outgoing sync request Process.Call within handle_[cast|call|info] it locks the internal mutex

until it receives the answer.

How it can happen in the details...

  1. start GenServer (A) with the default size of the mailbox
  2. send some message to this A
  3. in handle_info make a call to some process outside of this node. let it be B
  4. make some pause on B side with the answer
  5. flood A with any message (as we did it on the second step) just to overflow the mailbox (has a limit DefaultProcessMailboxSize = 100 )

now we got a block of writing to the mailbox due to the limit of mailbox size (buffered channel length). since this moment we are not able to receive and handle the answer we are waiting for on step 3. Usually, this sync call is limited by the timeout (default is 5 seconds, but can be customized via CallWithTimeout). It means during this period serving this link (between these nodes) is frozen.

Client side api to a Go based GenServer/Application: GenServer.call and GenServer.cast?

Hello, I just bumped into this project. Having some experience with Erlang/Elixir and also needing Go, this project would bridge a knowledge gap for sure.

When looking at the examples, I noticed they all show the receiving end of a message, which is processed by a HandleCall or HandleCast. In Erlang/Elixir, you typically write client side wrappers, meaning functions which call into GenServer.call or GenServer.cast.

Does this project also provide the Go based counterpart of the call and cast functions to send messages to the mailbox of a process?

How to Call a gen.ServerProcess?

Thank you for the great work you have done, I'm very excited about this library.

I think there is a problem with documentation. On pkg.go.dev there is an old version of documentation.

I browsed all the examples and I couldn't find information on how to make a Call request to a gen server process. What I wish is to get a process by name and then make a Call to it so that HandleCall will fire. In erlang it's easy and can be achieved with gen_server:call(pid,...) but in ergo it seams that I need to get hold of the gen.ServerProcess struct instance which appears to not be exposed.

Please help me with that.

Ergo receives DIST packet along with the HANDSHAKE final packet.

Discussed in #115

Originally posted by xiaodoudou12 November 18, 2022
my OTP version is Erlang/OTP 24 [erts-12.1.5] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit].
golang version is go1.18.ergo version is v1.999.211.
https://github.com/ergo-services/ergo/blob/v1.999.211/proto/dist/handshake.go:247

			case 'a':
				// 'a' + 16 (digest)
				if len(buffer) != 17 {
					return details, fmt.Errorf("malformed handshake ('a' length of digest)")
				}

				// 'a' + 16 (digest)
				digest := genDigest(dh.challenge, cookie)
				if bytes.Compare(buffer[1:17], digest) != 0 {
					return details, fmt.Errorf("malformed handshake ('a' digest)")
				}

				// handshaked
				return details, nil

sometime len(buffer) will more than 17
when like this, first receive 3 byte,then 45 byte,then 17 bytes, it work ok.
img_v2_044d6763-9d91-4ac6-ae8c-97e1e04083fg
but like this,first recevie 3 byte, then 45 byte, then 122 bytes, it not work.
img_v2_57cb63cb-3593-4908-87ff-4f388e172fbg
maybe handshake should think about TCP Stick Package.
maybe handshake should only need 17 bytes, the last bytes need take to distProto connection to read

Command timed out when CallRPC is used

Hello.

I've installed erlang@22 via homebrew and started an erlang node:

➜ ~ /usr/local/opt/erlang@22/bin/erl -sname demo@MBP-Alexandr -setcookie 123 -kernel logger_level info
Erlang/OTP 22 [erts-10.7.2.8] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [hipe] [dtrace]

=PROGRESS REPORT==== 28-Apr-2021::18:57:43.349637 ===
    application: kernel
    started_at: 'demo@MBP-Alexandr'
=PROGRESS REPORT==== 28-Apr-2021::18:57:43.354807 ===
    application: stdlib
    started_at: 'demo@MBP-Alexandr'
Eshell V10.7.2.8  (abort with ^G)
(demo@MBP-Alexandr)1>

And then I use the following code to send a command via RPC:

		log.Println("Attempt")
		q, e := demoGS.process.CallRPCWithTimeout(30, "demo@MBP-Alexandr", "erlang", "node")
		log.Println(q, e)
		process.Kill()

But I always get an error: timeout

So logs from demo erlang node have the following text:

(demo@MBP-Alexandr)1> =ERROR REPORT==== 28-Apr-2021::18:59:43.240434 ===
** Node 'test@MBP-Alexandr' not responding **
** Removing (timedout) connection **

And logs from go-erlang:

2021/04/28 18:58:03 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:node
2021/04/28 18:58:03 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/04/28 18:58:03 [test@MBP-Alexandr] registering peer &{demo@MBP-Alexandr [<nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil>] 0 12 {0 0}}
2021/04/28 18:58:03 Node control: etf.Tuple{6, etf.Pid{Node:"demo@MBP-Alexandr", ID:0x36, Serial:0x0, Creation:0x2}, "", "global_name_server"}
2021/04/28 18:58:03 [test@MBP-Alexandr] sending message by name global_name_server
2021/04/28 18:58:03 [test@MBP-Alexandr] sending message by pid {test@MBP-Alexandr 1003 1 1}
2021/04/28 18:58:03 [test@MBP-Alexandr]. {test@MBP-Alexandr 1003 1 1} got message from etf.Pid{Node:"demo@MBP-Alexandr", ID:0x36, Serial:0x0, Creation:0x2}
2021/04/28 18:58:03 GLOBAL_NAME_SERVER: HandleCast: etf.Tuple{"init_connect", etf.Tuple{5, -576460752303423488}, "demo@MBP-Alexandr", etf.Tuple{"locker", "no_longer_a_pid", etf.List{}, etf.Pid{Node:"demo@MBP-Alexandr", ID:0x37, Serial:0x0, Creation:0x2}}}

Why does it happen? Can I use RPC calls?

Compilation to arm fails

Describe the bug
Compiling to ARM does not work

To Reproduce

$ GOARCH=arm go build

Expected behavior
Compilation works fine

Actual behaviour
This error is displayed:

# github.com/ergo-services/ergo/lib/osdep
../../go/pkg/mod/github.com/ergo-services/[email protected]/lib/osdep/linux.go:15:11: invalid operation: usage.Utime.Sec * 1000000000 + usage.Utime.Nano() (mismatched types int32 and int64)
../../go/pkg/mod/github.com/ergo-services/[email protected]/lib/osdep/linux.go:16:11: invalid operation: usage.Stime.Sec * 1000000000 + usage.Stime.Nano() (mismatched types int32 and int64)
# github.com/ergo-services/ergo/lib
../../go/pkg/mod/github.com/ergo-services/[email protected]/lib/tools.go:166:11: cannot use 4294967000 (untyped int constant) as int value in assignment (overflows)

Environment (please complete the following information):

  • Arch: arm
  • OS: Linux
  • Framework Version [v1.999.210]
  • Number of CPU or (GOMAXPROCS not set)

Additional context
Removing GOARCH fixes it however I need to run few services on ARM

some problems

Several problems were found:

  1. All the mailbox messages in the process are processed by coroutines. At present, there is a situation where the main process crashes unexpectedly, but the messages in the Mailbox can be processed in additional ways.

pid

  1. Multiple Ergo nodes communicate, and some nodes restart back and forth. Accidentally, after the restart, nodes cannot connect to each other, and Erlang nodes cannot ping Ergo nodes

RPC call doesn't use timeout when an erlang node is down [v1.2.2]

Hello!

I've found a new defect after 1.2.2.

You can easily reproduce it. Just call CallRPCWithTimeout function when an erlang node is down.

Log:

2021/05/11 09:52:16 [test@MBP-Alexandr] RPC calling: demo@MBP-Alexandr:erlang:system_info
2021/05/11 09:52:16 [test@MBP-Alexandr] sending message by tuple [rex demo@MBP-Alexandr]
2021/05/11 09:52:16 [test@MBP-Alexandr] can't connect to demo@MBP-Alexandr: Can't resolve port for demo@MBP-Alexandr: desired node not found

And the app is stuck

Migrate to go module

It is easier to use go modules which was introduced in go 1.11 for development and working with dependecies in projects.
Please take a look: https://github.com/golang/go/wiki/Modules
It would be greate to migrate this project to go modules
What do you think about that?

Have a nice day!

Panic invalid memory address after reconnect a node

Describe the bug
Using the example genstage, change it to have two nodes, connect one to the other, disconnect, reconnect and panic

It kills the producer process directly, the entire node does NOT crash

  • Open two terminals
  • First one: go run ./producer
  • Second one: go run ./consumer
  • They should communicate and trigger events
  • CTRL + C on consumer
  • Restart it again
  • The error should show up
2023/01/06 17:06:25 WARNING! Server terminated <336B493D.0.1011>["producer"]. Panic reason: "invalid memory address or nil 

To Reproduce
Steps to reproduce the behavior:

I've made a gist with all the code and a readme there https://gist.github.com/davidroman0O/db04fa224c710a4ae33d53d28304e88b

Expected behavior
Maybe a better error why it has a nil pointer? why? where? stacktrace?

Or maybe it should just not panic

Environment (please complete the following information):

  • OS: Tested on Windows 10 and QubeOS 4.11
  • Framework Version v1.999.220
  • Number of CPU 12

Additional context

I was just experimenting with the examples, trying to plug a bit of my codebase of some side projects and end up with this issue
I came back on the examples to test it again with very little modification and the error appear!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.