{"action":"created","issue":{"url":"https://api.github.com/repos/haskell-distributed/network-transport/issues/23","labels_url":"https://api.github.com/repos/haskell-distributed/network-transport/issues/23/labels{/name}","comments_url":"https://api.github.com/repos/haskell-distributed/network-transport/issues/23/comments","events_url":"https://api.github.com/repos/haskell-distributed/network-transport/issues/23/events","html_url":"https://github.com/haskell-distributed/network-transport/issues/23","id":90049262,"number":23,"title":"Enable GC of connections without losing ordering constraints","user":{"login":"mboes","id":51356,"avatar_url":"https://avatars.githubusercontent.com/u/51356?v=3","gravatar_id":"","url":"https://api.github.com/users/mboes","html_url":"https://github.com/mboes","followers_url":"https://api.github.com/users/mboes/followers","following_url":"https://api.github.com/users/mboes/following{/other_user}","gists_url":"https://api.github.com/users/mboes/gists{/gist_id}","starred_url":"https://api.github.com/users/mboes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mboes/subscriptions","organizations_url":"https://api.github.com/users/mboes/orgs","repos_url":"https://api.github.com/users/mboes/repos","events_url":"https://api.github.com/users/mboes/events{/privacy}","received_events_url":"https://api.github.com/users/mboes/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/haskell-distributed/network-transport/labels/Feature%20Request","name":"Feature Request","color":"ededed"},{"url":"https://api.github.com/repos/haskell-distributed/network-transport/labels/network-transport","name":"network-transport","color":"ededed"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2015-06-22T09:08:12Z","updated_at":"2015-06-22T09:08:12Z","closed_at":null,"body":"_From @edsko on November 7, 2012 9:56_\n\nWhen process A sends a message to process B, we must open a connection from A to B and, currently,  *keep that connection open* for the duration of A's lifetime in order to maintain ordering guarantees. If A sends messages to lots of different processes (think of a server responding to clients) this will result in a space leak.\r\n\r\nWhat we need is a way to garbage collect connections when they are no longer used, but still maintain ordering guarantees. There are (at least) two ways in which we might implement this:\r\n\r\n* *Client side*. When we close the connection, we wait for an acknowledgement from the other side that the connection has been closed. Once we have received this acknowledgement we know that all messages we sent have been received and hence it's safe to open a new connection. If the sender starts sending more messages before receiving the acknowledgement this messages must be buffered. \r\n\r\n  We could implement this at the Cloud Haskell level, and have the *node controller* send the acknowledgement. This introduces the question \"what about connections to the node controller itself\". We already have implicit reconnect to and from node controllers, implying potential message loss, but what we *don't* want is reordering of messages to node controllers. \r\n\r\n  We could instead implement it at the Network.Transport level and introduce a new `OutgoingConnectionClosed` event, but there is a technical difficulty here too: outgoing connections don't have identifiers on the sender side, only on the receiver side, so we somehow need to be able identify to the sender *which* connection got closed, without making `connect` synchronous. (The sender allocates part of the receiver-side ID, but only half of it.) One easy solution is to introduce\r\n\r\n      closeWithAck :: Connection -> Int64 -> IO ()\r\n\r\n  so that we allow the sender to provide a token which will be returned in the `OutgoingConnectionClosed` event.\r\n\r\n* *Server side*. When we close the connection, we remember the connection ID (but see discussion above: we don't know connection IDs sender side -- similar solutions could be proposed here). Then when we open a new connection we first send a message saying \"all messages you receive on this connection must be delivered after you have got all messages on connection *X*\"; this implies server side buffering.\r\n\r\n  Of course, this by itself doesn't gain us much because now we still have to maintain state: the connection ID of the last connection to every process we ever sent a message to. So the question becomes when can we forget this state? This seems to require some sort of acknowledgement from the server, though.\r\n\r\nClient side seems the easier way to solve this problem.\n\n_Copied from original issue: haskell-distributed/distributed-process#64_"},"comment":{"url":"https://api.github.com/repos/haskell-distributed/network-transport/issues/comments/114046431","html_url":"https://github.com/haskell-distributed/network-transport/issues/23#issuecomment-114046431","issue_url":"https://api.github.com/repos/haskell-distributed/network-transport/issues/23","id":114046431,"user":{"login":"mboes","id":51356,"avatar_url":"https://avatars.githubusercontent.com/u/51356?v=3","gravatar_id":"","url":"https://api.github.com/users/mboes","html_url":"https://github.com/mboes","followers_url":"https://api.github.com/users/mboes/followers","following_url":"https://api.github.com/users/mboes/following{/other_user}","gists_url":"https://api.github.com/users/mboes/gists{/gist_id}","starred_url":"https://api.github.com/users/mboes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mboes/subscriptions","organizations_url":"https://api.github.com/users/mboes/orgs","repos_url":"https://api.github.com/users/mboes/repos","events_url":"https://api.github.com/users/mboes/events{/privacy}","received_events_url":"https://api.github.com/users/mboes/received_events","type":"User","site_admin":false},"created_at":"2015-06-22T09:08:12Z","updated_at":"2015-06-22T09:08:12Z","body":"_From @edsko on November 7, 2012 9:59_\n\nA related issue is *when* do we garbage collect connections. One option is to clear them periodically after some timeout period. Another possibility is to collect them when process A has no further references process B (using weak references somehow). The main difficulty there is that ProcessId at the moment is a stateless object, and *must be* as long as we have pure decoding (ProcessIds are serializable, after all), unless we resort to `unsafePerformIO` trickery."}}