How can I find the PID associated with a monitor Reference?

Using HTTPoison as my example case, there are times when I possess a #Reference<…>, but my process wasn’t the one that setup the monitor that returned the reference.

For instance:

iex(1)> %HTTPoison.AsyncResponse{id: ref} = HTTPoison.get!("http://localhost:11000/events", %
{}, stream_to: self)
%HTTPoison.AsyncResponse{id: #Reference<0.0.1.2036>}
iex(2)> ref
#Reference<0.0.1.2036>

That’s fine, but as this is the Docker event stream, I can conceive of a situation in which a container is stopped but not deleted. This sounds OK, but when this happens the streaming connection to the Docker API persists until container deletion. Eventually, given enough stopped containers that weren’t deleted (particularly on a busy Docker host), I’ll end up with a boatload of connections leftover that aren’t doing me any good.

Is there a way to find the PID that’s reference in these #Reference types? I can’t seem to find a way to kill the associated PID by using the #Reference returned by HTTPoison, nor to dereference the item so that I can track down the PID to kill it the normal way. I’ve crawled the Elixir and Erlang code bases to try and find some way to hack through to the PID, but I’m stumped.

1 Like

So as it turns out, I don’t necessarily need this for my current use case after all, as HTTPoison defaults to a 5000ms recv_timeout. This causes the connection to close with an error once no messages have been received within a 5000ms window. This is fine for my needs, as once a container is shutdown the API will stop streaming events, and my connections will clean themselves up if I handle the close messages properly.

Now, that said - I’d still love to know if my original desire is possible! :slight_smile: Always happy to learn something new!

1 Like

There is no general way of getting the PID of a process if all you have is a reference. References in Erlang and Elixir are nothing more than unique IDs (within the lifetime of a single node, so don’t use them as keys in a db or something else persistent :slight_smile:). They are indeed returned when calling Process.monitor/1, but that’s just an implementation detail of monitors that could be replaced with another method of generating a unique ID.

In the case of HTTPoison, it is just hackney’s (the underlying HTTP client that HTTPoison uses) way of keeping track of a connection. I’m not really familiar with hackney’s internals, but after a look at its source code, it seems that hackney creates a reference, because it automatically tries to reconnect when a connection closes, possibly using a new/other process for the reconnected socket and uses an ETS table for mapping a reference to the current PID.

If my assumption is right, it would mean that having the PID of a connection is not safe, as the connection might be reconnected and using a difference process afterwards. However, hackney seems to have a public function for explicitly closing a connection, so this might work:

:hackney.close(response.id)

where response is of type %HTTPoison.AsyncResponse{}

Keep in mind I haven’t tested the above if it actually does what you need :slight_smile:

-vincent

2 Likes

Aha! I don’t know why it never occurred to me to dig into HTTPoison’s (and in turn, hackney’s) handling of its own references. :slight_smile:

For the moment, I don’t have need of it, but I’ll play with it and see what it does under the hood. Thanks so much for taking the time to respond!