Skip to content

Commit

Permalink
2.3.1 (#6)
Browse files Browse the repository at this point in the history
* update readme
* make `emitter.notify` boolean return just like `emitter.emit`
* supposedly fix a bug for `HamokEmitter` appears when a remote endpoint shuts down and makes the subscription inconsistent for the newcomers due to a locally executed removal process. Instead we added a handler and used the deleterequest internally to make the map consistent
* make `appData` in Hamok optional
  • Loading branch information
balazskreith authored Aug 19, 2024
1 parent 18f2d42 commit 8eec5f8
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 107 deletions.
30 changes: 11 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ yarn add hamok
- [Important Notes](#important-notes)
- [Contributing](#contributing)
- [License](#license)

## Quick Start

```javascript
Expand All @@ -39,28 +40,22 @@ import { Hamok } from 'hamok';
(async () => {
const server_1 = new Hamok();
const server_2 = new Hamok();

server_1.on('message', server_2.accept.bind(server_2));
server_2.on('message', server_1.accept.bind(server_1));

server_1.addRemotePeerId(server_2.localPeerId);
server_2.addRemotePeerId(server_1.localPeerId);

server_1.start();
server_2.start();


await Promise.all([
new Promise(resolve => server_1.once('leader-changed', resolve)),
new Promise(resolve => server_2.once('leader-changed', resolve)),
server_1.join(),
server_2.join()
]);

const storage_1 = server_1.createMap<string, number>({
mapId: 'my-replicated-storage',
});
const storage_2 = server_2.createMap<string, number>({
mapId: 'my-replicated-storage',
});

console.log('Setting value in storage on server_1 for key-1 to 1');
console.log('Setting value in storage on server_2 for key-2 to 2');

Expand All @@ -72,7 +67,7 @@ import { Hamok } from 'hamok';
server_1.waitUntilCommitHead(),
server_2.waitUntilCommitHead(),
])

console.log('value for key-2 by server_1:', storage_1.get('key-2'));
console.log('value for key-1 by server_2:', storage_1.get('key-1'));

Expand All @@ -89,11 +84,11 @@ Hamok is a lightweight, distributed object storage library developed using the [

[Raft](https://raft.github.io/) is a consensus algorithm designed to manage a replicated log across a distributed system. Its primary goal is to ensure that multiple servers agree on a sequence of state transitions, providing consistency and fault tolerance in distributed systems. RAFT breaks down the consensus problem into three subproblems:

- **Leader Election:** Ensures that one server acts as the leader, which is responsible for managing the log replication.
- **Leader Election:** Ensures that one server acts as the leader, which is responsible for managing the log replication.

- **Log Replication:** The leader receives log entries from clients and replicates them to follower servers. The leader waits for a majority of followers to acknowledge the entries before considering them committed.
- **Log Replication:** The leader receives log entries from clients and replicates them to follower servers. The leader waits for a majority of followers to acknowledge the entries before considering them committed.

- **Safety:** RAFT guarantees that committed log entries are durable and will not be lost, even in the presence of server failures. It ensures that no two leaders can be elected for the same term and that logs are consistent across servers.
- **Safety:** RAFT guarantees that committed log entries are durable and will not be lost, even in the presence of server failures. It ensures that no two leaders can be elected for the same term and that logs are consistent across servers.

Overall, RAFT is designed to be understandable and easy to implement while providing strong consistency and reliability in distributed systems.

Expand All @@ -105,7 +100,6 @@ Hamok uses Raft to manage the shared storage across multiple instances.
- **Distributed Data Structures:** Provides maps, queues, records, and emitters.
- **Event-driven Architecture:** Emits events for state changes, errors, and communication.


## Collections

### HamokMap
Expand All @@ -124,12 +118,10 @@ HamokEmitter is an event emitter designed for distributed systems. It allows ser

HamokRecord is a feature that provides distributed storage for individual record objects. Each record can be accessed and updated by multiple service instances, with RAFT ensuring that all updates are consistently applied and persisted across the system.


## User Manual

You can find detailed user manuals [here](https://balazskreith.github.io/hamok-ts/)


## Contributing

Contributions are welcome! Please feel free to submit issues or pull requests to improve the library.
Expand Down
132 changes: 69 additions & 63 deletions docs/emitter.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
## User Manual
[Hamok](./index.md) | HamokEmitter | [HamokMap](./map.md) | [HamokQueue](./queue.md) | [HamokRecord](./record.md)

[Hamok](./index.md) / HamokEmitter / [HamokMap](./map.md) / [HamokQueue](./record.md) / [HamokRecord](./remoteMap.md) / [HamokRemoteMap](./remoteMap.md)

## Table of Contents
* [Overview](#overview)
* [Configuration](#configuration)
* [API Reference](#api-reference)
* [Properties](#properties)
* [Events](#events)
* [Methods](#methods)
* [Examples](#examples)
* [FAQ](#faq)

- [Overview](#overview)
- [Configuration](#configuration)
- [API Reference](#api-reference)
- [Properties](#properties)
- [Events](#events)
- [Methods](#methods)
- [Examples](#examples)
- [FAQ](#faq)

## Overview

Expand All @@ -22,59 +23,64 @@ To create a `HamokEmitter` instance, you need a `Hamok` instance. Here is how yo

```typescript
const emitter = hamok.createEmitter<MyEventMap>({
emitterId: 'exampleEmitter',
emitterId: "exampleEmitter",
});
```

### Configuration

```typescript
type MyEventMap = {
myEvent: [string, number];
myEvent: [string, number];
};
const emitter = hamok.createEmitter<MyEventMap>({

/**
* The unique identifier for the emitter.
*/
emitterId: 'exampleEmitter',

/**
* Optional. The timeout duration in milliseconds for requests.
*
* DEFAULT: 5000
*/
requestTimeoutInMs: 5000,

/**
* Optional. The maximum waiting time in milliseconds for a message to be sent.
* The storage holds back the message sending if Hamok is not connected to a grid or not part of a network.
*
* DEFAULT: 10x requestTimeoutInMs
*/
maxMessageWaitingTimeInMs: 50000,

/**
* Optional. The maximum number of keys allowed in request or response messages.
*
* DEFAULT: 0 means infinity
*/
maxOutboundMessageKeys: 1000,

/**
* Optional. The maximum number of values allowed in request or response messages.
*
* DEFAULT: 0 means infinity
*/
maxOutboundMessageValues: 100,

/**
* Optional. A map of payload codecs for encoding and decoding event payloads.
* The key is an event type, and the value is a codec for that event type.
*
* DEFAULT: JSON codec
*/
payloadsCodec?: Map<keyof MyEventMap, { encode: (...args: unknown[]) => string, decode: (data: string) => unknown[] }>,
/**
* The unique identifier for the emitter.
*/
emitterId: "exampleEmitter",

/**
* Optional. The timeout duration in milliseconds for requests.
*
* DEFAULT: 5000
*/
requestTimeoutInMs: 5000,

/**
* Optional. The maximum waiting time in milliseconds for a message to be sent.
* The storage holds back the message sending if Hamok is not connected to a grid or not part of a network.
*
* DEFAULT: 10x requestTimeoutInMs
*/
maxMessageWaitingTimeInMs: 50000,

/**
* Optional. The maximum number of keys allowed in request or response messages.
*
* DEFAULT: 0 means infinity
*/
maxOutboundMessageKeys: 1000,

/**
* Optional. The maximum number of values allowed in request or response messages.
*
* DEFAULT: 0 means infinity
*/
maxOutboundMessageValues: 100,

/**
* Optional. A map of payload codecs for encoding and decoding event payloads.
* The key is an event type, and the value is a codec for that event type.
*
* DEFAULT: JSON codec
*/
payloadsCodec: Map<
keyof MyEventMap,
{
encode: (...args: unknown[]) => string;
decode: (data: string) => unknown[];
}
>,
});
```

Expand Down Expand Up @@ -117,15 +123,15 @@ A class for managing events and subscriptions in a distributed system.
```typescript
const emitter = new HamokEmitter(connection, payloadsCodec);

emitter.subscribe('event', (data) => {
emitter.subscribe("event", (data) => {
console.log(`Received data: ${data}`);
});

emitter.publish('event', 'sample data').then((peerIds) => {
emitter.publish("event", "sample data").then((peerIds) => {
console.log(`Event published to peers: ${peerIds}`);
});

emitter.unsubscribe('event', (data) => {
emitter.unsubscribe("event", (data) => {
console.log(`Unsubscribed from event`);
});

Expand All @@ -134,7 +140,7 @@ emitter.close();

## Examples

- [simple distributed emitter](https://github.com/balazskreith/hamok-ts/blob/main/examples/src/emitter-example.ts)
- [simple distributed emitter](https://github.com/balazskreith/hamok-ts/blob/main/examples/src/emitter-example.ts)

## FAQ

Expand All @@ -143,7 +149,7 @@ emitter.close();
To create a `HamokEmitter` instance, you need a `HamokConnection`. Here is an example:

```typescript
const connection = new HamokConnection('my-storage-id');
const connection = new HamokConnection("my-storage-id");
const emitter = new HamokEmitter<MyEventMap>(connection);
```

Expand All @@ -160,7 +166,7 @@ When creating a `HamokEmitter` instance, you can optionally pass a `payloadsCode
You can listen to `HamokEmitter` events using the `on` method. Here is an example:

```typescript
emitter.on('myEvent', (message, count) => {
emitter.on("myEvent", (message, count) => {
console.log(`Received: ${message} - ${count}`);
});
```
Expand Down Expand Up @@ -191,7 +197,7 @@ emitter.clear();
To subscribe to an event, use the `subscribe` method:

```typescript
await emitter.subscribe('myEvent', (message, count) => {
await emitter.subscribe("myEvent", (message, count) => {
console.log(`Received: ${message} - ${count}`);
});
```
Expand All @@ -201,7 +207,7 @@ await emitter.subscribe('myEvent', (message, count) => {
To unsubscribe from an event, use the `unsubscribe` method:

```typescript
await emitter.unsubscribe('myEvent', (message, count) => {
await emitter.unsubscribe("myEvent", (message, count) => {
console.log(`Received: ${message} - ${count}`);
});
```
Expand All @@ -211,7 +217,7 @@ await emitter.unsubscribe('myEvent', (message, count) => {
To publish an event, use the `publish` method:

```typescript
emitter.publish('myEvent', 'Hello, world!', 42);
emitter.publish("myEvent", "Hello, world!", 42);
```

### What is the difference between the `publish` and `notify` methods?
Expand All @@ -225,4 +231,4 @@ In short use notify for fire and forget messages, and use publish for messages t

### What is the payloadsCodec for?

The `payloadsCodec` is a map of payload codecs for encoding and decoding event payloads. The key is an event type, and the value is a codec for that event type. This is useful for customizing the encoding and decoding of event payloads, if you are for example unsatisfied with the default JSON encoding/decoding.
The `payloadsCodec` is a map of payload codecs for encoding and decoding event payloads. The key is an event type, and the value is a codec for that event type. This is useful for customizing the encoding and decoding of event payloads, if you are for example unsatisfied with the default JSON encoding/decoding.
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## User Manual

[Hamok](./index.md) | [HamokEmitter](./emitter.md) | [HamokMap](./map.md) | HamokQueue | [HamokRecord](./record.md) | [HamokRemoteMap](./remoteMap.md)
Hamok / [HamokEmitter](./emitter.md) / [HamokMap](./map.md) / [HamokQueue](./record.md) / [HamokRecord](./remoteMap.md) / [HamokRemoteMap](./remoteMap.md)

## Table of Contents

Expand Down
2 changes: 1 addition & 1 deletion docs/map.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## User Manual

[Hamok](./index.md) | [HamokEmitter](./emitter.md) | [HamokMap](./map.md) | HamokQueue | [HamokRecord](./record.md) | [HamokRemoteMap](./remoteMap.md)
[Hamok](./index.md) / [HamokEmitter](./emitter.md) / HamokMap / [HamokQueue](./record.md) / [HamokRecord](./remoteMap.md) / [HamokRemoteMap](./remoteMap.md)

## Table of Contents

Expand Down
2 changes: 1 addition & 1 deletion docs/queue.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## User Manual

[Hamok](./index.md) | [HamokEmitter](./emitter.md) | [HamokMap](./map.md) | HamokQueue | [HamokRecord](./record.md) | [HamokRemoteMap](./remoteMap.md)
[Hamok](./index.md) / [HamokEmitter](./emitter.md) / [HamokMap](./map.md) / HamokQueue / [HamokRecord](./remoteMap.md) / [HamokRemoteMap](./remoteMap.md)

## Table of Contents

Expand Down
2 changes: 1 addition & 1 deletion docs/record.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## User Manual

[Hamok](./index.md) | [HamokEmitter](./emitter.md) | [HamokMap](./map.md) | HamokQueue | [HamokRecord](./record.md) | [HamokRemoteMap](./remoteMap.md)
[Hamok](./index.md) / [HamokEmitter](./emitter.md) / [HamokMap](./map.md) / [HamokQueue](./record.md) / HamokRecord / [HamokRemoteMap](./remoteMap.md)

## Table of Contents

Expand Down
2 changes: 1 addition & 1 deletion docs/remoteMap.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## User Manual

[Hamok](./index.md) | [HamokEmitter](./emitter.md) | [HamokMap](./map.md) | HamokQueue | [HamokRecord](./record.md) | [HamokRemoteMap](./remoteMap.md)
[Hamok](./index.md) / [HamokEmitter](./emitter.md) / [HamokMap](./map.md) / [HamokQueue](./record.md) / [HamokRecord](./remoteMap.md) / HamokRemoteMap

## Table of Contents

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hamok",
"version": "2.3.0",
"version": "2.3.1",
"description": "Lightweight Distributed Object Storage on RAFT consensus algorithm",
"main": "lib/index.js",
"types": "lib/index.d.ts",
Expand Down
5 changes: 3 additions & 2 deletions src/Hamok.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export type HamokConfig<AppData extends Record<string, unknown> = Record<string,
/**
* A custom appData object to be used by the application utilizes Hamok.
*/
appData: AppData,
appData?: AppData,
}

/**
Expand Down Expand Up @@ -437,7 +437,7 @@ export class Hamok<AppData extends Record<string, unknown> = Record<string, unkn
}

public get appData(): AppData {
return this.config.appData;
return this.config.appData ?? {} as AppData;
}

public get localPeerId(): string {
Expand Down Expand Up @@ -915,6 +915,7 @@ export class Hamok<AppData extends Record<string, unknown> = Record<string, unkn
HamokMessageType.CLEAR_ENTRIES_REQUEST,
HamokMessageType.INSERT_ENTRIES_REQUEST,
HamokMessageType.REMOVE_ENTRIES_REQUEST,
HamokMessageType.DELETE_ENTRIES_REQUEST,
])
},
storageCodec,
Expand Down
Loading

0 comments on commit 8eec5f8

Please sign in to comment.