Brought to you by
Outrageous Performance
Max De Marzi
RageDB
Max De Marzi
Developing RageDB
■ maxdemarzi.com and ragedb.com
■ @maxdemarzi and @rage_database
■ github.com/maxdemarzi and github.com/ragedb
■ I am a graph addict and RageDB is a Graph Database
A Sad Story
12 Cores each, so 24 cores per server x 245 servers = 5880 Cores
Performance Test - 2 Hop Query
20,000 queries per second with almost 6000 cores.
20k queries, almost 6000 cores!!!
3.5 Queries per Second per Core
Every time they traverse a relationship, they have to take a network hit
I’ll tell you why…
I’ll tell you why…
So many cores!
Seastar
Seastar Framework
Open Source, C++ framework for server applications.
■ Shared Nothing Model
● Server per Core using “Shards”
■ Message Passing
■ Futures and Promises
■ High Performance Networking
■ DPDK (optional)
■ Includes an HTTP server
Distribute on Cores
Not on Servers
Distributing Graphs is Hard….NP-Hard
NP-
How?
Each core controls a Shard of the Graph
■ For each node, a single Shard controls:
● Its properties
● Its outgoing relationships
● The properties of its outgoing relationships
■ For each node, a single Shard copies:
● Its incoming relationship Links
■ The Shard that controls a Node or
Relationship messages other Shards
Every Node has a Type and Key
uint16_t Shard::CalculateShardId(const std::string &type, const std::string &key) const {
// We need to find where the node goes,
// so we use the hash of the type and key to create a 64 bit number
uint64_t x64 = std::hash<std::string>()((type + '-' + key));
// Then we bucket it into a shard depending on the number of cores we have
return (uint16_t)(((__uint128_t)x64 * (__uint128_t)cores) >> SIXTY_FOUR);
}
// From: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
■ Figure out Shard
● Hash and bucket into Shard
■ Shard adds to Vectors grouped by Type
● Creates an Internal Id for the Node
● To generate External Id for Node
■ Combine Internal Id, Node Type and Shard Id
■ Add Node Properties to Groups of Vectors
● One Vector per Property
● One Group per Node Type
● Use Internal Id for location
Add a Node
Flip flop between Internal and External IDs
unsigned int SHARD_BITS = std::bit_width(_cpus);
unsigned int SHARD_MASK = (1 << SHARD_BITS) - 1;
unsigned int TYPE_BITS = 16U;
// 64 bits: Up to 10 bits for core id (1,024),
// 16 bits for the type (65,536) and 38 bits for the id (274,877,906,944)
uint64_t Shard::internalToExternal(uint16_t type_id, uint64_t internal_id) {
return (((internal_id << TYPE_BITS) + type_id) << SHARD_BITS) +
seastar::this_shard_id();
}
uint64_t Shard::externalToInternal(uint64_t id) {
return (id >> (TYPE_BITS + SHARD_BITS));
}
Delete a Node
Rarest operation that happens in practice
■ Delete Incoming Relationships
● Group links by controlling Shard
● Message controlling Shard to delete their relationships
■ …and any properties they have
● Delete links
■ Delete Outgoing Relationships
● Group them by non-controlling Shard
● Delete their properties
● Message non-controlling Shard to delete their links
● Delete relationships
■ Delete Node Properties
■ Delete Node
4 Layer Design
■ HTTP + JSON Layer
● Provided by Seastar
■ Lua Layer
● In Thread
● Optional
■ Peered Layer
● Coordinates between Shards
■ Shard Layer
● Controls Data
Example Request ( Get Node pseudo code)
future<std::unique_ptr<reply>> Nodes::GetNodeHandler::handle(request, reply) {
return parent.graph.shard.local().NodeGetPeered(type, key)
.then([rep = std::move(rep)](Node node) mutable {
if (node.getId() == 0) {
reply->set_status(not_found);
return reply;
}
rep->write_body("json", stream_object(node_json(node)));
return reply;
});
}
Example Request ( NodeGetPeered code)
seastar::future<Node> Shard::NodeGetPeered(const std::string &type,
const std::string &key) {
uint16_t node_shard_id = CalculateShardId(type, key);
return container().invoke_on(node_shard_id, [type, key](Shard &local_shard) {
return local_shard.NodeGet(type, key);
});
}
HTTP + JSON Layer
Talk to it:
■ From your Browser
■ From any programming language
■ No Drivers needed
■ No Custom protocol
Lua Layer
“Moon” in Portuguese:
■ Proven Language
● Used in games
● Used in embedded systems
■ Fast
● Fastest embeddable scripting language
● Using LuaJIT
■ Powerful but Small
● Learn it faster than C++
■ Roblox
● Millions of Kids learning Lua
Lua Layer
Query Language:
■ Simple Queries
● Example: Get a Node
■ Last Line converted to JSON
Lua Layer
Query Language:
■ Pipelined Queries
● One or more
● Related or not
● Batches
Lua Layer
Query Language:
■ Complex Queries
■ One VM per Shard
■ Sandboxed
■ Using Sol2 C++ interface
Clear slide for diagram with caption
Can’t present at P99 Conf without some P99 Numbers
Can’t present at P99 Conf without some P99 Numbers
It’s Open Source under the Apache License
Brought to you by
Max De Marzi
max@ragedb.com
@maxdemarzi

Outrageous Performance: RageDB's Experience with the Seastar Framework

  • 1.
    Brought to youby Outrageous Performance Max De Marzi RageDB
  • 2.
    Max De Marzi DevelopingRageDB ■ maxdemarzi.com and ragedb.com ■ @maxdemarzi and @rage_database ■ github.com/maxdemarzi and github.com/ragedb ■ I am a graph addict and RageDB is a Graph Database
  • 3.
  • 5.
    12 Cores each,so 24 cores per server x 245 servers = 5880 Cores
  • 6.
    Performance Test -2 Hop Query
  • 7.
    20,000 queries persecond with almost 6000 cores. 20k queries, almost 6000 cores!!!
  • 8.
    3.5 Queries perSecond per Core
  • 9.
    Every time theytraverse a relationship, they have to take a network hit
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
    Seastar Framework Open Source,C++ framework for server applications. ■ Shared Nothing Model ● Server per Core using “Shards” ■ Message Passing ■ Futures and Promises ■ High Performance Networking ■ DPDK (optional) ■ Includes an HTTP server
  • 15.
  • 16.
    Distributing Graphs isHard….NP-Hard NP-
  • 17.
    How? Each core controlsa Shard of the Graph ■ For each node, a single Shard controls: ● Its properties ● Its outgoing relationships ● The properties of its outgoing relationships ■ For each node, a single Shard copies: ● Its incoming relationship Links ■ The Shard that controls a Node or Relationship messages other Shards
  • 18.
    Every Node hasa Type and Key uint16_t Shard::CalculateShardId(const std::string &type, const std::string &key) const { // We need to find where the node goes, // so we use the hash of the type and key to create a 64 bit number uint64_t x64 = std::hash<std::string>()((type + '-' + key)); // Then we bucket it into a shard depending on the number of cores we have return (uint16_t)(((__uint128_t)x64 * (__uint128_t)cores) >> SIXTY_FOUR); } // From: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
  • 19.
    ■ Figure outShard ● Hash and bucket into Shard ■ Shard adds to Vectors grouped by Type ● Creates an Internal Id for the Node ● To generate External Id for Node ■ Combine Internal Id, Node Type and Shard Id ■ Add Node Properties to Groups of Vectors ● One Vector per Property ● One Group per Node Type ● Use Internal Id for location Add a Node
  • 20.
    Flip flop betweenInternal and External IDs unsigned int SHARD_BITS = std::bit_width(_cpus); unsigned int SHARD_MASK = (1 << SHARD_BITS) - 1; unsigned int TYPE_BITS = 16U; // 64 bits: Up to 10 bits for core id (1,024), // 16 bits for the type (65,536) and 38 bits for the id (274,877,906,944) uint64_t Shard::internalToExternal(uint16_t type_id, uint64_t internal_id) { return (((internal_id << TYPE_BITS) + type_id) << SHARD_BITS) + seastar::this_shard_id(); } uint64_t Shard::externalToInternal(uint64_t id) { return (id >> (TYPE_BITS + SHARD_BITS)); }
  • 21.
    Delete a Node Rarestoperation that happens in practice ■ Delete Incoming Relationships ● Group links by controlling Shard ● Message controlling Shard to delete their relationships ■ …and any properties they have ● Delete links ■ Delete Outgoing Relationships ● Group them by non-controlling Shard ● Delete their properties ● Message non-controlling Shard to delete their links ● Delete relationships ■ Delete Node Properties ■ Delete Node
  • 22.
    4 Layer Design ■HTTP + JSON Layer ● Provided by Seastar ■ Lua Layer ● In Thread ● Optional ■ Peered Layer ● Coordinates between Shards ■ Shard Layer ● Controls Data
  • 23.
    Example Request (Get Node pseudo code) future<std::unique_ptr<reply>> Nodes::GetNodeHandler::handle(request, reply) { return parent.graph.shard.local().NodeGetPeered(type, key) .then([rep = std::move(rep)](Node node) mutable { if (node.getId() == 0) { reply->set_status(not_found); return reply; } rep->write_body("json", stream_object(node_json(node))); return reply; }); }
  • 24.
    Example Request (NodeGetPeered code) seastar::future<Node> Shard::NodeGetPeered(const std::string &type, const std::string &key) { uint16_t node_shard_id = CalculateShardId(type, key); return container().invoke_on(node_shard_id, [type, key](Shard &local_shard) { return local_shard.NodeGet(type, key); }); }
  • 25.
    HTTP + JSONLayer Talk to it: ■ From your Browser ■ From any programming language ■ No Drivers needed ■ No Custom protocol
  • 26.
    Lua Layer “Moon” inPortuguese: ■ Proven Language ● Used in games ● Used in embedded systems ■ Fast ● Fastest embeddable scripting language ● Using LuaJIT ■ Powerful but Small ● Learn it faster than C++ ■ Roblox ● Millions of Kids learning Lua
  • 27.
    Lua Layer Query Language: ■Simple Queries ● Example: Get a Node ■ Last Line converted to JSON
  • 28.
    Lua Layer Query Language: ■Pipelined Queries ● One or more ● Related or not ● Batches
  • 29.
    Lua Layer Query Language: ■Complex Queries ■ One VM per Shard ■ Sandboxed ■ Using Sol2 C++ interface
  • 30.
    Clear slide fordiagram with caption
  • 31.
    Can’t present atP99 Conf without some P99 Numbers
  • 32.
    Can’t present atP99 Conf without some P99 Numbers
  • 33.
    It’s Open Sourceunder the Apache License
  • 34.
    Brought to youby Max De Marzi max@ragedb.com @maxdemarzi