Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extracted Var from MVar abstraction. No added functionality yet
- Loading branch information
Merlijn Boogerd
committed
Nov 24, 2017
1 parent
24190ed
commit e9b7bf2
Showing
22 changed files
with
216 additions
and
215 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,40 @@ | |||
Variable programming | |||
==================== | |||
|
|||
# Introduction | |||
|
|||
We have gone through a transition of using immutable data instead of mutable data. This is due to negative publicity around using variables, most of which is warranted. | |||
|
|||
- Concurrent access to mutable datastructures is complicated to do in a way that is not prone to race conditions or other unintended effects | |||
- When sharing data between two separate modules in your codebase, a variable has to be made visible on the highest level of the two modules' respective height. Often also exposing the variable to modules that | |||
shouldn't be touching the data; in the limit introducing completely global variables. | |||
- One might want to take control of some of the complexities by introducing a separation between readers and writers. This requires distinct interfaces for every datastructure. i.e. an IntWriter and IntReader, or DoubleWriter and DoubleReader | |||
|
|||
One convenience of variables is that for some problems they feel more intuitive than their immutable counterpart, and may be more performant on typical computer architectures as well. | |||
|
|||
One way of solving these issues is reifying variables as a `Var`, making explicit the fact that it can be `set`d and `sample`d at any point in time: | |||
|
|||
- Access to the variable is no longer done through its scope; the variable can be passed around as an object | |||
- What modules receive the power to write or read can be easily controlled using the more general `VarWriter` and `VarReader` interfaces | |||
- Having wrapped an arbitrary variable datastructure in our `Var`, we can implement its accessors in a concurrency-safe and performant manner, i.e. `AtomicReference` | |||
|
|||
Additional advantages that can be gained through this abstraction: | |||
|
|||
- functorial `map` to derive `Var`s from a base `Var` using an arbitrary function | |||
- `inflate`/`zip` as a means of lifting the datastructure in a heterogeneous list, or expanding the list | |||
- applicative `ap` as a means of deriving new `Var`s from multiple inputs, not unlike the `Signal` pattern in functional reactive programming. | |||
- producing a `ReactivePublisher` such that all updates to the variable can be explicitly observed. | |||
|
|||
Note that each of these can be implemented in a concurrency-safe and performant way using Akka Streams (Lightbend's reactive-streams implementation) | |||
|
|||
# Monotonic Variables | |||
|
|||
We will refer to the subset of datastructures whose operations are monotonic as monotonic datastructures. Their operations inflate the datastructure in a way that enables eventual consistency. For every | |||
such datastructure a `merge` operation exists that is `commutative`, `associative` and `idempotent`, i.e. it is well behaved under re-ordering of operations or multiple executions thereof, which frequently | |||
occur in a network setting. | |||
|
|||
Monotonic variables add another layer of safety, warranting their use in a truly global context. Here they are not limited to being global on a single machine, but they can be shared between _all_ machines, | |||
with the guarantee that all updates that occur will eventually be reflected on all other machines sharing that variable, assuming those machines have moments of healthy operation where they can receive and | |||
aid in the propagation of those updates. The speed with which such replication occurs is dependent on a number of factors, such as the number of replicas, the health of said replicas, and the efficiency of | |||
the replication mechanism under those conditions. The advantage of monotonic variables is that replication can be implemented as an independent responsibility, separate from the application programmer that | |||
queries and/or updates the variables. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
66 changes: 66 additions & 0 deletions
66
mem/src/main/scala/io/demograph/monotonic/var/AtomicMVar.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,66 @@ | |||
/* | |||
* Copyright 2017 Merlijn Boogerd | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
|
|||
package io.demograph.monotonic.`var` | |||
|
|||
import java.util.function.UnaryOperator | |||
|
|||
import algebra.lattice.{ BoundedJoinSemilattice, JoinSemilattice } | |||
import io.demograph.monotonic.queue.{ MergingQueue, Queue } | |||
import org.reactivestreams.Subscriber | |||
|
|||
/** | |||
* A variable whose content is monotonic (all operations are monotonic), and whose content can be accessed / manipulated | |||
* in an atomic fashion. | |||
*/ | |||
class AtomicMVar[V: JoinSemilattice](initialValue: V) extends AtomicVar[V](initialValue) with MVar[V] { | |||
|
|||
/** | |||
* Note that `set` here means really `inflate`. The method ensures that the update is an inflation w.r.t. the | |||
* previous state. | |||
*/ | |||
override protected[`var`] def _set(delta: V): V = { | |||
val previous = ref.getAndUpdate(new UnaryOperator[V] { | |||
override def apply(currentState: V): V = JoinSemilattice.join(currentState, delta) | |||
}) | |||
updateSubscribers(enqueue(delta) andThen dispatchReadyElements) | |||
previous | |||
} | |||
|
|||
/** | |||
* @param f The function to apply (note that this function should be without side-effects. | |||
* In the current implementation, this function _will_ be applied twice! | |||
* @return The previous state of this `MVar` | |||
*/ | |||
override protected[`var`] def _update(f: V ⇒ V): V = { | |||
val oldState = ref.getAndUpdate(new UnaryOperator[V] { | |||
override def apply(currentState: V): V = JoinSemilattice.join(currentState, f(currentState)) | |||
}) | |||
updateSubscribers(enqueue(f(oldState)) andThen dispatchReadyElements) | |||
oldState | |||
} | |||
|
|||
override protected def queueFromCurrentState = new MergingQueue[V](Some(sample)) | |||
} | |||
|
|||
object AtomicMVar { | |||
|
|||
def apply[V: JoinSemilattice](initialValue: V): MVar[V] = new AtomicMVar[V](initialValue) | |||
|
|||
def apply[V: BoundedJoinSemilattice](): MVar[V] = apply(BoundedJoinSemilattice[V].zero) | |||
|
|||
case class SubscriberState[V](subscriber: Subscriber[V], demand: Long, queue: Queue[V]) | |||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.