Skip to content

Commit

Permalink
Update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
rstefan1 committed Apr 12, 2024
1 parent fccd4f9 commit 137356f
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 67 deletions.
145 changes: 94 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,106 +8,149 @@ This is an implementation of the Bimodal Multicast Protocol written in GO.
You can synchronize all types of messages: bool, string, int,
complex structs, etc.

---

## Overview

The Bimodal Multicast Protocol runs in a series of rounds.

At the beginning of each round, every node randomly chooses another node and
sends it a digest of its message histories. The message is called gossip
message.

The node that receive the gossip message compares the given digest with the
messages in its own message buffer.

If the digest differs from its message histories, then it send a message
back to the original sender to request the missing messages. This message is
called solicitation.

---

## Usage

* Imports
- ### Step 1: Imports

```golang
import (
"github.com/rstefan1/bimodal-multicast/pkg/bmmc"
)
```go
import "github.com/rstefan1/bimodal-multicast/pkg/bmmc"
```

* Configure the protocol

```golang
cfg := bmmc.Config{
Addr: "localhost",
Port: "14999",
Callbacks: map[string]func (interface{}, *log.Logger) error {
"awesome-callback":
func (msg interface{}, logger *log.Logger) error {
fmt.Println("The message is:", msg)
return nil
},
},
BufferSize: 2048,
}
```
- ### Step 2: Configure the host

* Create an instance for protocol
The host must implement [Peer interface](https://github.com/rstefan1/bimodal-multicast/blob/f98c69dbc8ac22decdb438a1d6b5abc4b5db2db0/pkg/internal/peer/peer.go#L20):

```golang
p, err := bmmc.New(cfg)
```go
type Peer interface {
String() string
Send(msg []byte, route string, peerToSend string) error
}
```

* Start the protocol
- ### Step 3: Configure the bimodal-multicast protocol

```go
cfg := bmmc.Config{
Host: host,
Callbacks: map[string]func (interface{}, *log.Logger) error {
"custom-callback":
func (msg interface{}, logger *log.Logger) error {
fmt.Println("The message is:", msg)

```golang
err := p.Start()
return nil
},
},
Beta: float64,
Logger: logger,
RoundDuration: time.Second * 5,
BufferSize: 2048,
}
```

* Stop the protocol
| Config | Required | Description |
|---------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Host | Yes | Host of Bimodal Multicast server. <br/>Must implement [Peer interface](https://github.com/rstefan1/bimodal-multicast/blob/f98c69dbc8ac22decdb438a1d6b5abc4b5db2db0/pkg/internal/peer/peer.go#L20). Check the previous step. |
| Callback | No | You can define a list of callbacks.<br/>A callback is a function that is called every time a message on the server is synchronized. |
| Beta | No | The beta factor is used to control the ratio of unicast to multicast traffic that the protocol allows. |
| Logger | No | You can define a [structured logger](https://pkg.go.dev/log/slog). |
| RoundDuration | No | The duration of a gossip round. |
| BufferSize | Yes | The size of messages buffer.<br/>The buffer will also include internal messages (e.g. synchronization of the peer list).<br/>***When the buffer is full, the oldest message will be removed.*** |

```golang
p.Stop()
```

* Add a new message in buffer (When the buffer is full, the oldest message will be removed.)
- ### Step 4. Create a bimodal multicast server

```golang
err := p.AddMessage("awesome message", "awesome-callback")

err := p.AddMessage(12345, "awesome-callback")

err := p.AddMessage(true, "awesome-callback")
```go
bmmcServer, err := bmmc.New(cfg)
```

For messages without callback, you can use `bmmc.NOCALLBACK` as callback type.
- ### Step 5. Create the host server (e.g. a HTTP server)

The server must handle a list of predefined requests.
Each of these handlers must read the message body and call a predefined function.

* Get all messages from the buffer
| Handler route | Function to be called |
|-------------------|-------------------------------------------|
| `bmmc.GossipRoute` | `bmmcServer.GossipHandler(body)` |
| `bmmc.SolicitationRoute` | `bmmcServer.SolicitationHandler(body)` |
| `bmmc.SynchronizationRoute` | `bmmcServer.SynchronizationHandler(body)` |

```golang
messages := p.GetMessages()
For more details, check the [exemples](#examples).

- ### Step 6. Start the host server and the bimodal multicast server

```go
# Start the host server
hostServer.Start()

# Start the bimodal multicast server
bmmcServer.Start()
```

<a name="custom_anchor_name"></a>
- ### Step 7. Add a message to broadcast

```go
bmmcServer.AddMessage("new-message", "my-callback")
bmmcServer.AddMessage(12345, "another-callback")
bmmcServer.AddMessage(true, bmmc.NOCALLBACK)
```

* Add a new peer in peers buffer.
- ### Step 8. Retrieve all messages from buffer

```golang
err := p.AddPeer("localhost", "18999")
```go
bmcServer.GetMessages()
```

* Remove a peer from peers buffer
- ### Step 9. Add/Remove peers

```golang
err := p.RemovePeer("localhost", "18999")
```go
bmmcServer.AddPeer(peerToAdd)
bmmcServer.RemovePeer(peerToRemove)
```

* Get all peers
- ### Step 10. Stop the bimodal multicast server

```golang
peers := GetPeers()
```go
bmmcServer.Stop()
```

---

## Examples

<a name="examples"></a>

1. using a [http server](_examples/http)
2. using a [maelstrom server](_examples/maelstrom)

---

## Contributing

I welcome all contributions in the form of new issues for feature requests, bugs
or even pull requests.

---

## License

This project is licensed under Apache 2.0 license. Read the [LICENSE](LICENSE) file
Expand Down
16 changes: 8 additions & 8 deletions _examples/http/bmmc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,33 +172,33 @@ var _ = Describe("BMMC with HTTP Server", func() {
It("sync buffers", func() {
// Add a message in first node.
// Both nodes must have this message.
Expect(bmmc1.AddMessage("awesome-first-message", "my-callback")).To(Succeed())
Expect(bmmc1.AddMessage("my-first-message", "my-callback")).To(Succeed())

Eventually(getBufferFn(bmmc1)).Should(ConsistOf(
[]string{
"awesome-first-message",
"my-first-message",
},
))
Eventually(getBufferFn(bmmc2)).Should(ConsistOf(
[]string{
"awesome-first-message",
"my-first-message",
},
))

// Add a message in second node.
// Both nodes must have this message.
Expect(bmmc2.AddMessage("awesome-second-message", "my-callback")).To(Succeed())
Expect(bmmc2.AddMessage("my-second-message", "my-callback")).To(Succeed())

Eventually(getBufferFn(bmmc1)).Should(ConsistOf(
[]string{
"awesome-first-message",
"awesome-second-message",
"my-first-message",
"my-second-message",
},
))
Eventually(getBufferFn(bmmc2)).Should(ConsistOf(
[]string{
"awesome-first-message",
"awesome-second-message",
"my-first-message",
"my-second-message",
},
))
})
Expand Down
2 changes: 1 addition & 1 deletion _examples/http/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func main() { //nolint: funlen, cyclop, gocyclo, gocognit
case "add-message":
if len(args) != 3 { //nolint: gomnd
fmt.Println("Invalid command. The `add-message` command must be in form: " +
"add-message awesome-message first-callback")
"add-message my-message first-callback")

break
}
Expand Down
12 changes: 7 additions & 5 deletions pkg/bmmc/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,22 @@ var errInvalidBufSize = errors.New("invalid buffer size")
// Config is the config for the protocol.
type Config struct {
// Host is the host peer.
// Required.
Host peer.Peer
// Beta is the expected fanout for gossip rounds
// Beta is the expected fanout for gossip rounds.
// Optional
Beta float64
// Logger
// Logger.
// Optional
Logger *slog.Logger
// Callbacks functions
// Callbacks functions.
// Optional
Callbacks map[string]func(any, *slog.Logger) error
// Gossip round duration
// Gossip round duration.
// Optional
RoundDuration time.Duration
// Buffer size
// Buffer size.
// When the buffer is full, the oldest message will be removed.
// Required
BufferSize int
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/bmmc/gossiper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ var _ = Describe("Gossiper", func() {

msgBuf := buffer.NewBuffer(25)

msg, err := buffer.NewElement("awesome message", "awesome-callback", false)
msg, err := buffer.NewElement("my message", "my-callback", false)
Expect(err).ToNot(HaveOccurred())

Expect(msgBuf.Add(msg)).To(Succeed())
Expand Down
2 changes: 1 addition & 1 deletion pkg/internal/peer/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ limitations under the License.

package peer

// Peer ...
// Peer is the interface of Host Peer.
type Peer interface {
String() string
Send(msg []byte, route string, peerToSend string) error
Expand Down

0 comments on commit 137356f

Please sign in to comment.