Let's start Scheme

2016-10-19

Connection and session model

CAUTION: It's rubbish. Just my brain storming.

I'm not even sure if this is a proper model name or not. But I often see in low level communication specification, there is a connection and sessions (or channels) belong to the connection. Sometimes (or most of the time?) channels can be recovered on different connections, but here I discuss non recoverable channel for my ease.

I've written couple of scripts which handles this type of things already. For example AMQP. I think one of the pros for this model is preventing actual connection count. Recently, I've faced a concurrency problem caused by reading data from one single connection. At that moment, I didn't want to create much of connections (but in the end realised that there's no other way, though). Then I've started thinking about it.

Now I'm thinking can this be done in generic way or at least something which can be reused instead of writing the same mode everywhere? Suppose,a connection have a port. If a session is derived from the connection, then connection gives the session a session id or so. Whenever data are read from sessions, then actual connection dispatches read data according to the required session id. Something like this:
(import (rnrs))

(define-record-type connection
  (fields port ;; suppose this is input/output port
          sessions)
  (protocol (lambda (p)
              (lambda (port)
                (p port (make-eqv-hashtable))))))
(define-record-type session
  (fields id connection))

(define (open-connection port) (make-connection port))
(define (open-session connection)
  (let* ((sessions (connection-sessions connection))
         ;; FIXME
         (id (+ (hashtable-size sessions) 1))
         (session (make-session id connection)))
    (hashtable-set! sessions id session)
    session))

(define (read-from-session session)
  ;; IMPLEMENT ME
  (read-for-session
   (session-connection session)
   (session-id session)))
To keep it clear, here connection is the physical connection and sessions are logical layers derived from a connection. A channel is a communication pipe between connection and session.

Obviously, this doesn't work as it is even if I implement reading part, because:
  1. There's no way to know which message should go which session
  2. There's no negotiation how to open a session, this type of things are usually client-server model.
To know how to dispatch messages, connections need to know how message frames look like. So connections should have something like receive-frame field which contains a procedure receiving a message frame?
(define-record-type connection
  (fields port
          ;; suppose this returns 2 values, message id and payload
          receive-frame
          negotiate-session
          sessions)
  (protocol (lambda (p)
              (lambda (port receive-frame negotiate-session)
                (p port receive-frame  negotiate-session (make-eqv-hashtable))))))
Is it good enough? But what if there are like these 2 process is simultaneously running; opening a session and reading payload from a session. So it might be a good idea to have channel like this?
(define-record-type connection
  (fields port
          receive-frame
          ;; takes session-id, channel-id and connection.
          ;; returns physical-session-id
          negotiate-session
          sessions
          channels)
  (protocol (lambda (p)
              (lambda (port receive-frame negotiate-session)
                (p port receive-frame
                   negotiate-session
                   (make-eqv-hashtable)
                   (make-eqv-hashtable))))))
(define-record-type session
  (fields id channel-id connection
          (mutable physical-session-id)))
(define-record-type channel
  (fields id buffer))

(define (open-connection port) (make-connection port))
(define (open-session connection)
  (let* ((sessions (connection-sessions connection))
         (channels (connection-channels connection))
         ;; FIXME
         (session-id (+ (hashtable-size sessions) 1))
         (channel-id (+ (hashtable-size channels) 1))
         (channel (make-channel channel-id
                                ;; IMPLEMENT ME
                                (make-buffer)))
         (session (make-session session-id channel-id connection #f)))
    (hashtable-set! sessions session-id session)
    (hashtable-set! channels channel-id channel)
    session))
Hmmm, would it be enough? I'm not sure yet. And I'm not even sure if this is useful. Maybe I need to implement a prototype.

No comments:

Post a Comment