• Stars
    star
    1,618
  • Rank 27,692 (Top 0.6 %)
  • Language
    Elixir
  • License
    MIT License
  • Created about 7 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Fast batch message passing between nodes for Erlang/Elixir.

Manifold

Master Hex.pm Version

Erlang and Elixir make it very easy to send messages between processes even across the network, but there are a few pitfalls.

  • Sending a message to many PIDs across the network also copies the message across the network that many times.
  • Send calls cost about 70 Β΅s/op so doing them in a loop eventually gets too expensive.

Discord runs a single GenServer per Discord server and some of these ~100,000 PIDs connected to them from many different Erlang nodes. Increasingly we noticed some of them getting behind on processing their message queues and the culprit was the cost of 70 Β΅s per send/2 call multiplied by connected sessions. How could we solve this?

Inspired by a blog post about boosting performance of message passing between nodes, Manifold was born. Manifold distributes the work of sending messages to the remote nodes of the PIDs, which guarantees that the sending processes at most only calls send/2 equal to the number of involved remote nodes. Manifold does this by first grouping PIDs by their remote node and then sending to Manifold.Partitioner on each of those nodes. The partitioner then consistently hashes the PIDs using :erlang.phash2/2, groups them by number of cores, sends to child workers, and finally those workers send to the actual PIDs. This ensures the partitioner does not get overloaded and still provides the linearizability guaranteed by send/2.

The results were great! We observed packets/sec drop by half immediately after deploying. The Discord servers in question also were finally able to keep up with their message queues.

Packets Out Reduction

Usage

Add it to mix.exs

defp deps do
  [{:manifold, "~> 1.0"}]
end

Then just use it like the normal send/2 except it can also take a list of PIDs.

Manifold.send(self(), :hello)
Manifold.send([self(), self()], :hello)

Options

When using Manifold there are two performance options you can choose to enable.

pack_mode

By default Manifold will send the message using vanilla External Term Format. If pack_mode is not specified or is set to nil or :etf then this mode will be used.

When messages are very large pack_mode can be set to :binary and this will cause Manifold to ensure that the term is only converted into External Term Format once. The encoding will happen in the sending process (either the calling process or a Manifold.Sender, see send_mode for additional details) and will

The :binary optimization works best with large and deeply nested messages that are being sent to PIDs across multiple nodes. Without the optimization, a message sent to N nodes will have to be translated into External Term Format N times. With large data structures, this operation can be expensive. With the optimization enabled, the encoding into binary happens once and then over distribution each sending process only needs to transmit the binary.

To send using the binary pack mode, just add the pack_mode argument

Manifold.send(self(), :hello, pack_mode: :binary)

send_mode

By default Manifold will send the message over the network from the caller process. If send_mode is not specified then the default behavior of sending from the caller process will be used.

When messages are very large, send_mode can be set to :offload which offloads the send from the calling process to a pool of Manifold.Sender processes. To maintain the linearizability guaranteed by send/2, the same calling process always offloads the work to the same Manifold.Sender process. The size of the Manifold.Sender pool is configurable.

This send mode is optional because its benefits are workload dependent. The optimization works best when the cost of sending a message to a local process < the cost of sending a message over distribution. This is most common for messages that are very large in size. For some workloads, it might degrade overall performance. Use with caution.

Caution: To maintain the linearizability guaranteed by send/2, do not mix calls to Manifold with and without offloading. Mixed use of the two different send modes to the same set of receiving nodes would break the linearizability guarantee.

To use the :offload send mode, make sure the Manifold.Sender pool size is appropriate for the workload:

config :manifold, senders: <size>

Then:

Manifold.send(self(), :hello, send_mode: :offload)

Configuration

Manifold takes a single configuration option, which sets the module it dispatches to actually call send. The default is GenServer. To set this variable, add the following to your config.exs:

config :manifold, gen_module: MyGenModule

In the above instance, MyGenModule must define a cast/2 function that matches the types of GenServer.cast.

License

Manifold is released under the MIT License. Check LICENSE file for more information.

More Repositories

1

discord-api-docs

Official Discord API Documentation
Markdown
5,543
star
2

lilliput

Resize images and animated GIFs in Go
C++
1,923
star
3

sorted_set_nif

Elixir SortedSet backed by a Rust-based NIF
Elixir
1,532
star
4

discord-open-source

List of open source communities living on Discord
JavaScript
1,319
star
5

focus-rings

A centralized system for displaying and stylizing focus indicators anywhere on a webpage.
TypeScript
1,120
star
6

fastglobal

Fast no copy globals for Elixir & Erlang.
Elixir
1,097
star
7

discord-rpc

C++
983
star
8

airhornbot

The only bot for Discord you'll ever need.
TypeScript
851
star
9

semaphore

Fast semaphore using ETS.
Elixir
718
star
10

react-dnd-accessible-backend

An add-on backend for `react-dnd` that provides support for keyboards and screenreaders by default.
TypeScript
576
star
11

ex_hash_ring

A fast consistent hash ring implementation in Elixir.
Elixir
475
star
12

discord-example-app

Basic Discord app with examples
JavaScript
434
star
13

OverlappingPanels

Overlapping Panels is a gestures-driven navigation UI library for Android
Kotlin
420
star
14

SimpleAST

Extensible Android library for both parsing text into Abstract Syntax Trees and rendering those trees as rich text.
Kotlin
360
star
15

discord-interactions-js

JS/Node helpers for Discord Interactions
TypeScript
345
star
16

instruments

Simple and Fast metrics for Elixir
Elixir
295
star
17

focus-layers

Tiny React hooks for isolating focus within subsections of the DOM.
TypeScript
292
star
18

discord-api-spec

OpenAPI specification for Discord APIs
237
star
19

discord-oauth2-example

Discord OAuth2 Example
Python
225
star
20

loqui

RPC Transport Layer - with minimal bullshit.
Rust
220
star
21

erlpack

High Performance Erlang Term Format Packer
Cython
211
star
22

cloudflare-sample-app

Example discord bot using Cloudflare Workers
JavaScript
197
star
23

use-memo-value

Reuse the previous version of a value unless it has changed
TypeScript
170
star
24

zen_monitor

Efficient Process.monitor replacement
Elixir
167
star
25

deque

Fast bounded deque using two rotating lists.
Elixir
141
star
26

avatar-remix-bot

TypeScript
127
star
27

linked-roles-sample

JavaScript
119
star
28

punt

Punt is a tiny and lightweight daemon which helps ship logs to Elasticsearch.
Go
113
star
29

embedded-app-sdk

πŸš€ The Discord Embedded App SDK lets you build rich, multiplayer experiences as Activities inside Discord.
TypeScript
109
star
30

sample-game-integration

An example using Discord's API and local RPC socket to add Voice and Text chat to an instance or match based multiplayer game.
JavaScript
105
star
31

endanger

Build Dangerfiles with ease.
TypeScript
96
star
32

discord-interactions-python

Useful tools for building interactions in Python
Python
93
star
33

react-base-hooks

Basic utility React hooks
TypeScript
77
star
34

dynamic-pool

a lock-free, thread-safe, dynamically-sized object pool.
Rust
76
star
35

itsdangerous-rs

A rust port of itsdangerous!
Rust
72
star
36

gen_registry

Simple and efficient local Process Registry
Elixir
71
star
37

confetti-cannon

Launch Confetti
TypeScript
45
star
38

discord-react-forms

Forms... in React
JavaScript
43
star
39

discord-interactions-php

PHP utilities for building Discord Interaction webhooks
PHP
40
star
40

babel-plugin-define-patterns

Create constants that replace various expressions at build-time
JavaScript
39
star
41

eslint-traverse

Create a sub-traversal of an AST node in your ESLint plugin
JavaScript
30
star
42

rapidxml

Mirror of rapidxml from http://rapidxml.sourceforge.net/
C++
29
star
43

memory_size

Elixir
29
star
44

gamesdk-and-dispatch

Public issue tracker for the Discord Game SDK and Dispatch
22
star
45

dispenser

Elixir library to buffer and send events to subscribers.
Elixir
17
star
46

eslint-plugin-discord

Custom ESLint rules for Discord
JavaScript
16
star
47

chromium-build

Python
15
star
48

hash_ring

hash_ring
C
14
star
49

limited_queue

Simple Elixir queue, with a constant-time `size/1` and a maximum capacity
Elixir
13
star
50

perceptual

A smarter volume slider scale
TypeScript
13
star
51

discord_vigilante

12
star
52

heroku-sample-app

Example discord bot using Heroku
JavaScript
11
star
53

postcss-theme-shorthand

Converts `light-` and `dark-` prefixed CSS properties into corresponding light/dark theme globals
JavaScript
11
star
54

babel-plugin-strip-object-freeze

Replace all instances of Object.freeze(value) with value
JavaScript
10
star
55

libyuv

our fork of libyuv for webrtc
C++
10
star
56

lilliput-rs

Lilliput, in Rust!
Rust
9
star
57

lilliput-bench

Benchmarker for lilliput
Python
8
star
58

sqlite3

Mirror of sqlite amalgamation from https://www.sqlite.org/
C
7
star
59

redis-py

Redis Python Client
Python
7
star
60

openh264

C++
6
star
61

slate-react-package-fork

TypeScript
6
star
62

rlottiebinding-ios

rlottie ios submodule
Starlark
5
star
63

jemalloc_info

A small library for exporting jemalloc allocation data in Elixir
Elixir
5
star
64

libnice

Fork of https://nice.freedesktop.org/wiki/
C
5
star
65

libvpx

C
4
star
66

libsrtp

Fork of libsrtp
C
4
star
67

RLottieAndroid

C++
4
star
68

opentelemetry-rust-datadog

Rust
4
star
69

slate-package-fork

JavaScript
4
star
70

lilliput-dep-source

Convenient source repo for Lilliput's dependencies
3
star
71

slate-hotkeys-package-fork

3
star
72

rules_ios

Bazel rules for building iOS applications and frameworks
Starlark
2
star
73

cocoapods-bazel

A Cocoapods plugin for automatically generating Bazel BUILD files
Ruby
2
star
74

eslint-plugin-react-discord

Fork of eslint-plugin-react
JavaScript
1
star