Skip to content

Commit

Permalink
MS documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
EltonCN committed Dec 2, 2024
1 parent abb63f2 commit ce45936
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 3 deletions.
7 changes: 7 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@
self
src/Differences from CST-Java.md

.. toctree::
:maxdepth: 4
:caption: Features
:hidden:

src/Memory Storage.md

.. toctree::
:maxdepth: 1
:caption: Examples
Expand Down
56 changes: 56 additions & 0 deletions docs/src/Memory Storage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Memory Storage

Memory Storage is a CST synchonization mechanism to synchonize memories across multiple CST instances, whether they are CST-Java or CST-Python instances.

Synchronization is performed using a Redis server. A server reachable by all instances must be running to use Memory Storage. Redis can be installed on Linux and Windows (using WSL) using [Redis documentation](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/).

When using Memory Storage, each local CST instance is called a node. Memories with the same name in participating nodes are synchronized. Only memories that are used by more than one node are stored in the storage. Other memories are only indicated as existing in the storage, and can be transferred later if another node starts using them.

The collection of synchonized nodes is a mind, and a single Redis instance can support multiple minds with unique names.

To use it, you just need to add a :class:`Memory Storage Codelet<cst_python.memory_storage.memory_storage>` to each mind participating in the network.

## Protocol

This section presents the messages used for the operation of the Memory Storage. It is intended only for CST developers.

### Mind nodes

`<mind_name>:nodes` is a Redis set containing all the nodes names. When a node enters the network, it needs to add it's unique name to this set.

### Memory Lifecycle and Storage

Initially, no memory is stored in Memory Storage. In this case, memories are stored without data, only indicating which node it is stored in, called the owner. When another node creates a memory that already exists in Memory Storage, it checks its owner. If it is a node, it requests the transfer of the memory. Once transferred, the memory is stored in Memory Storage.

Each memory in the storage is stored in a Redis hash `<mind_name>:memories:<memory_name>` with the keys:

- `evaluation`: memory evaluation
- `I`: memory info
- `id`: memory id
- `owner`: current node owning the memory. If is in the storage, the value is set to "".
- `logical_time`: time when the memory was stored.

When a local Memory Storage Codelet detects a new created memory, it checks if a corresponding hash `<mind_name>:memories:<memory_name>` exists. If so, it checks the owner. If it is a node, it requests the transfer of memory. After ensuring that the memory is in the storage, it performs the synchronization. If the created memory does not have a corresponding memory in the storage, the node sends an impostor containing only the owner set as its own name.

#### Memory Transfer

Each node subscribes to two Redis channels to perform memory transfers:

- `<mind_name>:nodes:<node_name>:transfer_memory`: receives transfer requests. Each request is a string containing a JSON. It must contain the "request" field, with subfields "memory_name" indicating which memory should be transferred, and "node" indicating which node requests the transfer. Optionally, it can contain a "logical_time" field with the time of the requesting node when making the request. After making the transfer, the node responds by sending a message on the requesting node's transfer done channel.
- `<mind_name>:nodes:<node_name>transfer_done:` Receives messages indicating that a requested memory transfer has been performed. The message is a string containing a JSON, with a "request" field and a "memory_name" subfield indicating which memory was transferred. Optionally, it can contain a "logical_time" field indicating the time when the transfer was performed.

A node waits for a transfer until a certain timeout. If it does not receive a response, it sends its own version of the memory to the Memory Storage.

Transferred memories are marked with `owner=""`, and are synchronized with each Memory Storage Codelet cycle on all nodes that have a version of this memory.

All nodes that have memory in the storage also subscribe to the memory update channel, `< mind_name>:memories:<memory_name>:update`.

#### Memory Update

When a node is synchronizing a memory, it checks each Memory Storage Codelet cycle to see if it has been updated locally, comparing the local memory timestamp with the last update. If it has been updated, it initiates an update.

In an update, the logical memory time in the storage is first obtained and compared with the logical memory time of the local memory. If the version in the storage is more recent, it retrieves the remote data. If the local version is more recent, it sends it to the storage and sends a message on the memory update channel.

Messages received on the memory update channel also initiate updates.

Memory Storage attempts to ensure that the most recent versions of memory are maintained, but overwrites can occur if a memory is updated concurrently on two nodes. Verification of which memory is more recent is done using logical clocks.
5 changes: 3 additions & 2 deletions src/cst_python/memory_storage/memory_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@

class MemoryStorageCodelet(Codelet):
'''
Synchonizes local memories with a Redis database.
Synchonizes local memories with a Redis database.
When using MemoryStorage, each local CST instance is called a node.
When using MemoryStorage, each local CST instance is called a node.
Memories with the same name in participating nodes are synchronized.
The collection of synchonized nodes is a mind.
A single Redis instance can support multiple minds with unique names
Expand Down
2 changes: 1 addition & 1 deletion tests/examples/test_activation_and_monitoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def test_activation(tb :TestbookNotebookClient):
else:
expected_sensory = input_value * 10

assert math.isclose(sensory_output, expected_sensory, abs_tol=0.3)
assert math.isclose(sensory_output, expected_sensory, abs_tol=0.35)

last_sensory_output = sensory_output

Expand Down

0 comments on commit ce45936

Please sign in to comment.