Server - Downlinks

Swim developer logo developer.

Downlinks


A SWIM Downlink is a read- and write-capable, stateful subscription to a Lane of some Service instance. Downlinks are resilient to network congestion and failures, and they are multiplexed to handle multiple connections to different Lanes in a Service instance over a single connection.

Downlink is an abstract term. There are two concrete Downlinks we use depending on the "target" Lane's type:

  • ValueDownlinks subscribe to ValueLanes
  • MapDownlinks subscribe to MapLanes

Downlinks are parameterized with the same types as their target Lanes. For example, a MapLane<Long, String> should be accessed via a MapDownlink<Long, String>.

A JoinLane is a Service-specific abstraction that enables viewing a collection of aggregated Downlinks as a single Lane. More information on JoinLanes can be found here.

Synchronous Functions


Reading from or writing to a Lane via a Downlink requires the same method call as doing so on the Lane itself, only invoked from the Downlink.

ValueDownlinks

For a ValueDownlink<T> link subscribed to a ValueLane<T> lane

  • T link.get() returns the current element in lane, identically to lane.get().
  • void link.set(T value) sets lane to value, identically to lane.set(T value).

MapDownlinks

For a MapDownlink<K,V> link subscribed to a MapLane<K,V> lane

  • V link.get(K key) returns the element mapped from key in lane, identically to lane.get(K key).
  • void link.put(T value) sets the mapping inside lane of key to value, identically to lane.set(T value).

Some things to note:

  • get(), set(), and put() should only be invoked once the Downlink's didLink() callback has been invoked.
  • Whenever you yourself writing link.get(), first ask whether you can accomplish your goal with the didSet() callback instead. Asynchronous callbacks always offer more real-time capabilities than synchronous invocations.

Callbacks


Similarly to a Lane, each Downlink comes with several overridable callbacks that will execute during various stages of the Downlink's lifecycle.

Lane Modification

  • didSet(newValue, oldValue) on a ValueDownlink is logically equivalent to ValueLane.didSet().
  • didUpdate(key, newValue, oldValue) on a MapDownlink is logically equivalent to MapLane.didUpdate().
More information on Lane callbacks can be found here.

Network Connectivity

Because working with Downlinks potentially introduces the network as a point of failure, both ValueDownlinks and MapDownlinks come with the following callbacks to verify connectivity:

  • didLink() is invoked immediately after a Downlink's connection to the Lane is fully established.
  • One should only invoke get, set, and put through a Downlink after its didLink() is triggered. A simple, but not always viable, way to ensure this is to exclusively place these three method invocations inside didLink() callbacks.
  • didUnlink() is invoked if the SWIM Server shuts down a connection. This can happen if
    • The nodeUri/laneUri combination is not valid for the application running on the targeted SWIM Server.
    • A network mishap interrupts an originally valid Downlink.
    Be aware that didUnlink() will NOT be invoked if there is no valid SWIM application running on the specified hostUri (because SWIM will interpret this as a network issue and retry with exponential backoff), so solely using didUnlink() to diagnose issues will likely result in false negatives.

Usage

Instantiation

Downlink instantiation is achieved via builder notation. For a given Downlink link:

  1. Parametrize link using keyClass() / keyForm() or valueClass() / valueForm() appropriately. This is not required for any recon.Value parameters.
  2. Build a qualified URI. At minimum, Downlinks must specify the desired Service instance's nodeUri and Lane's laneUri. When instantiated from within a Plane (such as inside a Lane callback), the hostUri will by default resolve to the Plane itself.
  3. Override any callbacks.
  4. Invoke link.open(); to initiate the network connection.

Teardown

When link has served its purpose and you no longer need any logic with its callbacks, simply call link.close(); to remove its view into the Lane.

Examples


Service

This snippet can be placed inside the SWIM Service, e.g. in its didStart() callback or inside any of its Lanes' callbacks. Given that

  1. The Plane containing the Service inside which we're writing has a ServiceType<?> field annotated with @SwimRoute("/service/:id")
  2. The aforementioned ServiceType<?> was built from a Service that has a MapLane<Long, Value> annotated with @SwimLane("history"),
this code will create a `MapDownlink` that eternally subscribes to the `MapLane`.
// Instantiation steps 1. and 2. final MapDownlink<Long, Value> link = downlinkMap().keyForm(Form.LONG) .nodeUri("/service/20") .laneUri("history") // Instantiation step 3. .didUpdate((k,n,o) -> { System.out.println("didUpdate triggered! " + "k: " + k + ", n: " + n.toRecon()); }) // Instantiation step 4. .open();
  • HTML
  • recon