Compare commits

..

251 Commits

Author SHA1 Message Date
Antonio Andelic
83495dea32 PoC for auth replication 2021-01-04 15:43:46 +01:00
Marko Budiselić
a75f2bfe37 Improve Jepsen run script (#64)
Co-authored-by: Antonio Andelic <antonio.andelic@memgraph.io>
2020-12-22 18:18:41 +01:00
antonio2368
d823ac6915 Add large Jepsen test (#67)
* Fix WAL recovery step
2020-12-21 10:59:50 +01:00
antonio2368
35ff0f58fb Add sequential test (#66) 2020-12-18 12:19:30 +01:00
antonio2368
8ec718a700 Small replication fixes (#59)
* Set state to invalid after exception

* Add proper locking

* Start background replicating only if in valid state

* Freeze transaction timestamp on replica

* Timeout fixes

* Fix Jepsen run script

* Disable perf checker and enable nemesis

* Add documentation for some chunks of code

* Decrease timeout so main doesn't hang on network partitions too long
2020-12-18 09:17:32 +01:00
Josip Seljan
323002e4f9 Fix replication queries (#65)
* Fix replication query visitors
* Improve error handling in ReplQueryHandler methods

Co-authored-by: jseljan <josip.seljan@memgraph.io>
2020-12-17 11:25:21 +01:00
antonio2368
62d5028c89 Jepsen test infrastracture improvements and bank test (#62)
* Define replication config for tests

* Add support for final generator

* Add bank test

* Add host name resolution and basic replication setup

* Add timeout support

* Define helper macros for replication tests

* Add nemesis configuration
2020-12-16 09:02:47 +01:00
Josip Seljan
229e24c8d3 Implement ReplicationQueryHandler class (#52)
* Refactor io::network::Endpoint class
* Add ParseSocketOrIpAddress static method to Endpoint class
* Implement ReplQueryHandler methods
* Add implementation of SetReplicationRole to ReplQueryHandler
* Fix PrepareReplicationQuery (create PullPlanVector)

Co-authored-by: jseljan <josip.seljan@memgraph.io>
2020-12-11 10:47:19 +01:00
Marko Budiselić
4a2eade101 Add Jepsen initialization (#53)
Co-authored-by: Antonio Andelic <antonio.andelic@memgraph.io>
2020-12-09 17:07:09 +01:00
Josip Seljan
8db0437659 Add support for specifying the replica port to SET REPLICATION ROLE query (#61)
Co-authored-by: jseljan <josip.seljan@memgraph.io>
2020-12-08 15:10:05 +01:00
antonio2368
38c42405b2 Improve error handling (#56) 2020-12-08 10:35:10 +01:00
antonio2368
8fd3d194ac Add configs and support for semi-sync and SSL (#55)
* Add config for replication client/server
* Add SSL to replication
* Add semi-sync replication
* Expose necessary information about replication
* Thread pool fix
* Set BasicResult value type to void
2020-12-08 10:35:10 +01:00
antonio2368
a86fb85515 Add epoch id and refactor replication client/server (#51) 2020-12-08 10:35:10 +01:00
Josip Seljan
12240ac356 Replace "CREATE REPLICA" query with "REGISTER REPLICA" (#50)
Co-authored-by: jseljan <josip.seljan@memgraph.io>
2020-12-08 10:35:10 +01:00
Josip Seljan
a40e0d81b3 Update nomenclature in the replication feature spec (#47)
* Update nomenclature in the replication feature spec

* Update the replication feature spec to contain the changes to the new replica registering syntax

Co-authored-by: jseljan <josip.seljan@memgraph.io>
2020-12-08 10:35:10 +01:00
antonio2368
30fb3270ed Define communication process (#49)
* Add basic communication process using commit timestamp
* Add file number to req
* Add proper recovery handling
* Allow loading of WALs with same seq num
* Allow always desired commit timestamp
* Set replica timestamp for operation
* Mark non-transactional timestamp as finished
2020-12-08 10:35:10 +01:00
Josip Seljan
473af1a139 Update replication queries to follow the new replication nomenclature (#48)
* MODE -> ROLE

Co-authored-by: jseljan <josip.seljan@memgraph.io>
2020-12-08 10:35:10 +01:00
antonio2368
dfb462a3d4 Use unfinished tasks num instead of idle thread num (#45) 2020-12-08 10:35:10 +01:00
antonio2368
76f1caea77 Add support for async replication (#41)
* Add thread pool
* Define async replication
* Expose replication state
* Rename TransactionHandler to ReplicaStream
2020-12-08 10:35:10 +01:00
antonio2368
12240b1dff Add replica recovery process (#40)
* Add file transfer over RPC
* Snapshot transfer implementation
* Allow snapshot creation only for MAIN instances
* Replica and main can have replication clients
* Use only snapshots and WALs that are from the Main storage
* Add flush lock and expose buffer
* Add fstat for file size and TryFlushing method
* Use lseek for size

Co-authored-by: Antonio Andelic <antonio.andelic@memgraph.io>
2020-12-08 10:35:10 +01:00
antonio2368
c55f2b6d47 Add safe file deletion utility FileRetainer (#38)
Co-authored-by: Antonio Andelic <antonio.andelic@memgraph.io>
2020-12-08 10:35:10 +01:00
antonio2368
7a1252d730 Add initial support for multiple clients (#31)
* Add tests for multiple clients
* Use variant for RPC server and clients
* Using synchronized list for replication clients, extracted variant access to a function
* Set MAIN as default, add unregister function, add a name for replication clients
* Use the regular list for clients
* Use test fixture so storage directory is cleaned
* Use seq_cst for replication_state

Co-authored-by: Antonio Andelic <antonio.andelic@memgraph.io>
2020-12-08 10:35:10 +01:00
Marko Budiselić
bc42a09f13 Add implementation of synchronous replication (#7)
This implements the initial version of synchronous replication.
Currently, only one replica is supported and that isn't configurable.

To run the main instance use the following command:
```
./memgraph \
    --main \
    --data-directory main-data \
    --storage-properties-on-edges \
    --storage-wal-enabled \
    --storage-snapshot-interval-sec 300
```

To run the replica instance use the following command:
```
./memgraph \
    --replica \
    --data-directory replica-data \
    --storage-properties-on-edges \
    --bolt-port 7688
```

You can then write/read data to Bolt port 7687 (the main instance) and also you
can read the data from the replica instance using Bolt port 7688.

NOTE: The main instance *must* be started without any data and the replica
*must* be started before any data is added to the main instance.

* Add basic synchronous replication test
* Using RWLock for replication stuff

Co-authored-by: Matej Ferencevic <matej.ferencevic@memgraph.io>
Co-authored-by: Antonio Andelic <antonio.andelic@memgraph.io>
2020-12-08 10:35:10 +01:00
Marko Budiselić
eb1f5bfc80 Add syntactic support for replication queries - prototype (#8)
Extend parser to handle replication queries - first implementation.

Co-authored-by: jseljan <josip.seljan@memgraph.io>
2020-12-08 10:35:10 +01:00
Marko Budiselić
8ffb2afad7 Write replication feature spec (#6)
Co-authored-by: Matej Ferencevic <matej.ferencevic@memgraph.io>
2020-12-08 10:35:10 +01:00
Marko Budiselić
53fa1482cb Update GHA jobs tags (#60) 2020-12-07 15:02:14 +01:00
antonio2368
c5f722b724 Document defining constructors for member variables (#54) 2020-12-02 10:11:51 +01:00
Marko Budiselić
573b6cb045 Clean and upgrade openCypher and stress tests (#27)
* Add initial behave test improvements
* Make HTML human-readable
* Remove tutorial tests
* Migrate to neo4j-driver==4.1.1
2020-11-24 13:09:14 +01:00
Marko Budiselić
4afcf1b655 Remove release examples (#42) 2020-11-23 15:46:03 +01:00
antonio2368
4e7c569071 Migrate code to C++20 (#44) 2020-11-17 11:03:08 +01:00
Marko Budiselić
ea27ac9391 Update workflow documentation (#36)
Co-authored-by: antonio2368 <antonio2368@users.noreply.github.com>
2020-11-12 20:45:29 +01:00
Marko Budiselić
958bc870b3 Migrate to toolchain-v2 (#33)
* GCC_VERSION=10.2.0
* BINUTILS_VERSION=2.35.1
* GDB_VERSION=10.1 (except centos-7 8.3)
* CMAKE_VERSION=3.18.4
* CPPCHECK_VERSION=2.2
* LLVM_VERSION=11.0.0
* SWIG_VERSION=4.0.2
2020-11-12 20:18:11 +01:00
antonio2368
f0382c82cd Potential memory leak fix (#39)
Co-authored-by: Antonio Andelic <antonio.andelic@memgraph.io>
2020-11-09 12:47:15 +01:00
Marko Budiselić
6fab357430 Upgrade cppitertools to v2.0 (#17) 2020-10-27 12:14:18 +01:00
antonio2368
f273be63a2 RPC client dangling reference fix (#34)
* Fix dangling reference
* Add docs for the dangling reference in lambda captures

Co-authored-by: Antonio Andelic <antonio.andelic@memgraph.io>
2020-10-26 13:33:48 +01:00
Marko Budiselić
9b984ab2f2 Add DISCARD fix to the CHANGELOG (#32) 2020-10-23 20:08:47 +02:00
Marko Budiselić
92bad4f62b Add docs examples as driver tests (#29) 2020-10-23 18:02:33 +02:00
Marko Budiselić
126c9d697b Fix pre-commit hook (#30) 2020-10-23 14:40:58 +02:00
Marko Budiselić
0478e89646 Fix package_deb_docker script (#28) 2020-10-23 09:37:41 +02:00
Marko Budiselić
814bb66ea6 Clean Memgraph repository (#12)
* Update documentation
* Update structure, CODEOWNERS, license files
* Update release/README
* Stop Actions if only docs file were updated
* Remove unused hidden files, move Doxygen logo
2020-10-21 18:56:15 +02:00
antonio2368
98f83e0c88 Rename mutex variable (#26)
Co-authored-by: Antonio Andelic <antonio.andelic@memgraph.io>
2020-10-20 17:26:07 +02:00
antonio2368
2e7d3822fe Disable SSL by default (#25)
* Print the connection type used (with/without SSL)
* Update CHANGELOG

Co-authored-by: Antonio Andelic <antonio.andelic@memgraph.io>
2020-10-20 15:49:10 +02:00
antonio2368
0a7d4278b1 Fix older version of SSL (#24)
* Add manual locking callback for SSL

Co-authored-by: Antonio Andelic <antonio.andelic@memgraph.io>
2020-10-20 12:55:13 +02:00
Marko Budiselić
291158160d Put all logrotate config to a file per offering (#23)
Not having one logrotate file produced an error during rpmlint.  It makes sense
to have one logrotate file after Memgraph is installed because it's easier to
manage config files. There are two logrotate files in the codebase, one for
Community and one for Enterprise edition. Having rotate files per offering also
makes sense because offerings are affected less often compared to the features.
It's easier to maintain.
2020-10-17 20:10:55 +02:00
Marko Budiselić
e525818355 Add v1.2 changelog (#18) 2020-10-16 15:49:37 +02:00
antonio2368
0bcc1d67bc Add support for Bolt v4(.1) (#10)
* Added handshake support
* Add support for v4 hello and goodbye
* Add support for pulling n results
* Add support for transactions
* Add pull n for the dump
* Add support for NOOP
* Add support for multiple queries
* Update bolt session to support qid
* Update drivers test with multiple versions and go
* Extract failure handling into a function
* Use unique ptr instead of optional for query execution
* Destroy stream before query execution

Co-authored-by: Antonio Andelic <antonio.andelic@memgraph.io>
2020-10-16 12:49:33 +02:00
antonio2368
42ac5d4ea3 Add datasets for music and StellarGraph tutorial (#22)
Co-authored-by: Antonio Andelic <antonio.andelic@memgraph.io>
2020-10-15 11:41:45 +02:00
Marko Budiselić
48587d6d5e Improve NetworkX module import (#21)
* Improve NetworkX module import
* Add Networkx dependencies to Dockerfiles
2020-10-15 09:14:50 +02:00
Marko Budiselić
157590a294 Fix mgbench comparison script (#20) 2020-10-15 09:03:48 +02:00
Marko Budiselić
7f179dc462 Fix release Dockerfile (#16) 2020-10-08 10:59:49 +02:00
Marko Budiselić
594f3ec1b0 Add check for all remote workers (#15) 2020-10-08 09:01:31 +02:00
Marko Budiselić
804a44dd6c Add check-build-system and a new workflow (#14) 2020-10-07 17:56:44 +02:00
antonio2368
1b50dc60f9 Add pre-commit hook (#13)
Co-authored-by: Antonio Andelic <antonio.andelic@memgraph.io>
2020-10-06 13:57:33 +02:00
llugovicmg
9d6b578237 Add the NetworkX query module (#5)
* Add __deepcopy__ for Properties
* Fix Vertices __contains__
* Check the incoming type in Vertex and Edge __eq__
* Add NetworkX support code
* Add NetworkX algorithms
* PageRank is now included in the nxalg module
* Rewrite graph analyzer to use NetworkX support code
* Don't make the error case be the "default" case
2020-10-01 20:51:55 +02:00
Josip Seljan
f7f861ca71 Add plan for label+property indexed node where property is not null (#2)
Replace ScanAll + Filter with a ScanAll variant performing label and property
lookup.

Co-authored-by: jseljan <josip.seljan@memgraph.io>
2020-10-01 13:22:21 +02:00
Matej Ferencevic
c12e4a49b1 Migrate deps to S3 (#3) 2020-09-22 19:52:26 +02:00
Matej Ferencevic
4c0fc11f69 Initial implementation of mgbench benchmarks (#4) 2020-09-22 18:55:28 +02:00
Matej Ferencevic
e0ffc533b9 Remove leftover Apollo files (#1) 2020-09-21 14:58:52 +02:00
Matej Ferencevic
0dcfdb9b89 Add GitHub workflows 2020-09-21 12:22:40 +02:00
Matej Ferencevic
1d973f7e31 Update dependencies in init script 2020-09-21 12:21:21 +02:00
Matej Ferencevic
4f4903803f Fix CentOS environment dependencies
Differential Revision: https://phabricator.memgraph.io/D2821
2020-09-18 17:18:27 +02:00
Matej Ferencevic
ff0d5962e1 Add missing Clang component to environment setup
Summary: Update signing keys

Differential Revision: https://phabricator.memgraph.io/D2820
2020-09-18 13:58:01 +02:00
Matej Ferencevic
04ceb8d4b1 Implement valueType openCypher function
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2818
2020-09-08 16:49:53 +02:00
Matej Ferencevic
fb7025a716 Clean-up awesome memgraph functions error handling
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2817
2020-09-08 13:16:07 +02:00
Matej Ferencevic
89b6262fa4 Fix multiline query concatenation character
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2814
2020-09-04 13:18:55 +02:00
Matej Ferencevic
d2ff465f8e Fix Cypher ID function Null handling
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2815
2020-09-04 13:18:08 +02:00
Matej Ferencevic
c12a87e9ca Allow inheritance of storage durability decoder
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2810
2020-08-17 11:12:59 +02:00
Matej Ferencevic
63de0b5db4 Allow inheritance of storage durability encoder
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2809
2020-08-17 10:23:39 +02:00
Matej Ferencevic
dd9180da32 Add request streaming support to the RPC client
Summary:
This change only adds streaming support to the client request. The client
response, server request and server response are still handled only when all of
the data is received.

Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2807
2020-08-14 16:19:48 +02:00
Matej Ferencevic
aaf0c1ca08 Improve SLK
Summary:
SLK now correctly handles different CPU architectures (BIG/little endian).
Also, more string encoding functions have been added.

Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2806
2020-08-14 15:27:20 +02:00
Matej Ferencevic
1513a455de Merge durability and storage
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2803
2020-08-14 15:08:43 +02:00
Matej Ferencevic
4f9e9aeafe Move snapshot/WAL encoding/decoding into their respective files
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2761
2020-08-14 14:17:45 +02:00
Matej Ferencevic
857de23687 Split storage durability implementation into multiple files
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2760
2020-08-14 13:36:52 +02:00
Matej Ferencevic
fba5d75bf4 Increase build timeout for CPack
Differential Revision: https://phabricator.memgraph.io/D2798
2020-07-01 17:17:52 +02:00
Matej Ferencevic
478acb7934 Add changelog for version 1.1
Reviewers: buda

Reviewed By: buda

Differential Revision: https://phabricator.memgraph.io/D2796
2020-07-01 16:42:16 +02:00
Matej Ferencevic
df27b8477d Remove unused UnwrapDegreeResult function
Reviewers: jseljan

Reviewed By: jseljan

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2797
2020-07-01 13:05:55 +02:00
Matej Ferencevic
02d3a04ffa Add Marvel Comic Universe tutorial data
Reviewers: buda, jseljan

Reviewed By: jseljan

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2783
2020-07-01 10:41:47 +02:00
Matej Ferencevic
4a53a31fcf Add FootballTransfers tutorial data
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2779
2020-07-01 09:24:35 +02:00
Matej Ferencevic
28590aea53 Fix variable expand between same symbol
Summary:
This diff fixes the variable expand operator to work correctly then the start
and destination nodes use the same symbol or when the destination symbol is an
existing symbol.

Previously, the variable expand operator produced paths that were both
completely wrong (they shouldn't have been produced) and nonexistent (they
didn't even exist in the storage). Invalid data was produced because of a
wrong equality check that was introduced in D1703.

This issue was reported externally and the supplied test case was:
```
CREATE (p1:Person {id: 1})-[:KNOWS]->(:Person {id: 2})-[:KNOWS]->(:Person {id: 3})-[:KNOWS]->(:Person {id: 4})-[:KNOWS]->(p1);
MATCH path = (pers:Person {id: 3})-[:KNOWS*2]->(pers) RETURN path;
```

Also, tests have been added so the behavior remains correct.

Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2793
2020-07-01 00:17:25 +02:00
Matej Ferencevic
0a2f2bfc90 Improve expansion performance
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2794
2020-06-29 16:37:11 +02:00
jseljan
a7b672ebbf Add TCK tests relating predicate functions
Summary:
Implement TCK regression-like tests relating 'any', 'all', and 'none'
predicate functions

Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2791
2020-06-29 12:56:59 +02:00
jseljan
9831f0396a Fix 'all' and 'single' functions; update their unit and TCK tests
Summary:
Semantics of 'all' and 'single' were updated to be
consistent with that of 'any' and 'none'

Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2789
2020-06-24 13:43:25 +02:00
jseljan
8bfebfca9d Add 'none' function implementation, its unit and TCK tests
Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2788
2020-06-23 13:06:06 +02:00
jseljan
21cee1eaec Fix any function implementation
Summary: Change any function's handling of Null elements in a list

Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2787
2020-06-19 15:40:57 +02:00
jseljan
098333f735 Add prototype implementation of the any function
Summary: Add any function prototype - no tests

Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2785
2020-06-10 17:01:24 +02:00
Matej Ferencevic
3dd393f8eb Fix multi-command transaction handling
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2784
2020-06-10 12:25:41 +02:00
Matej Ferencevic
6628d20e5a Make DUMP DATABASE work correctly in explicit transactions
Summary:
`DUMP DATABASE` used a separate transaction to read database data. That
wouldn't be an issue if the query was correctly disallowed in multicommand
transactions. Because it was allowed the output wasn't transactionally correct.
Instead of disabling `DUMP DATABASE` in multicommand transactions this change
fixes it so that it works properly in multicommand transactions.

Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2781
2020-06-08 15:56:16 +02:00
Matej Ferencevic
b923d2bc36 Integrate property store
Reviewers: buda

Reviewed By: buda

Subscribers: buda, pullbot

Differential Revision: https://phabricator.memgraph.io/D2764
2020-06-01 14:11:11 +02:00
Tonko Sabolcec
3932376301 Return properties as a list in SHOW CONSTRAINT INFO for unique constraints
Summary:
Before this change properties were joined by ", " and returned as a single string,
which was ambiguous for properties that contain ", ". This diff solves this
problem by returning properties as a list type.

Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2778
2020-05-29 12:59:33 +02:00
Marko Budiselic
d36da12b48 Update enterprise license
Summary:
In case something has to be updated in `release/LICENSE_ENTERPRISE.md`
please put your comments in the document marked with `UPCOMING`
inside this https://drive.google.com/drive/folders/1GbnryzrjkmNDVigac4d1x__JWbiqlxMD
folder because the content has to be valid from the legal standpoint
(it requires some legal review).

Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2770
2020-05-27 19:13:30 +02:00
Matej Ferencevic
a149f5bd81 Improve environment setup script
Summary:
The following improvements are in this diff:
 - Verify that run dependencies are correct
 - Setup gdbinit and install pahole
 - Add curl to CMake to enable HTTPS
 - Use HTTPS for LLVM download
 - Fix Debian 9 and Ubuntu 18.04 dependencies
 - Fix CentOS 7 sha256sum verification
 - Add support for Debian 10

Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2776
2020-05-25 11:05:01 +02:00
Matej Ferencevic
1a8ae7f9c2 Suppress empty query modules directory errors
Reviewers: llugovic, buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2771
2020-05-25 10:09:40 +02:00
Matej Ferencevic
b510ac1049 Fix WeightedShortestPath integer overflows
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2775
2020-05-25 10:09:19 +02:00
Tonko Sabolcec
59bc9d8989 Fix label/edge/property name escaping in DUMP DATABASE
Summary:
This diff fixes the issue for label name (and edge type/property)
with spaces and special characters to avoid possible OpenCypher injections.
Consider an example where label name is 'hello :world'. `DUMP DATABASE`
used to return query which creates a node (u:hello :world) - i.e. node
that contains two labels 'hello' and 'world'. This fix escapes names to
create the following node with exactly one label as expected:
```
(u:`hello :world`)
```

Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2774
2020-05-21 14:46:51 +02:00
Matej Ferencevic
b6dfb44b4c Fix various integer overflows in query
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2773
2020-05-21 14:30:01 +02:00
Matej Ferencevic
81ad5110ff Fix LIMIT and SKIP overflow bugs
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2772
2020-05-21 10:39:01 +02:00
Matej Ferencevic
f92dee6e7b Fix COUNT overflow bug
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2769
2020-05-20 16:57:17 +02:00
Matej Ferencevic
f047f55020 Implement IsPropertyEqual for property store
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2768
2020-05-20 15:41:13 +02:00
Lovro Lugovic
0c42bedf2f Add support for rescanning query modules
Reviewers: mferencevic, buda

Reviewed By: mferencevic, buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2765
2020-05-13 21:33:28 +02:00
Matej Ferencevic
7d9f741ceb Add missing optional check to property store
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2767
2020-05-06 10:07:58 +02:00
Matej Ferencevic
4d2eda398f Improve property store performance
Summary:
This diff improves the performance of `PropertyStore` with two main
techniques:

First:
`PropertyValue` has a very expensive constructor and destructor. The
`PropertyValue` was previously passed as a return value from many functions
wrapped in a `std::optional`. That caused the `PropertyValue`
constructor/destructor to be called for each intermediary value that was passed
between functions. This diff changes the functions to return a `bool` value
that imitates the `std::optional` "emptyness" flag and the `PropertyValue` is
modified using a pointer to it so that its constructor/destructor is called
only once.

Second:
The `PropertyStore` buffer was previously iterated through at least twice.
First to determine the exact position of the encoded property and then to
actually decode the property. This diff combines the two passes into a single
pass so that the property is immediately loaded if it is found.

Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2766
2020-05-05 10:55:00 +02:00
Lovro Lugovic
317f7f118c Make Python modules reloadable
Reviewers: mferencevic, buda

Reviewed By: mferencevic, buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2763
2020-04-27 17:29:58 +02:00
Marko Budiselic
f35225f26b Optimize IN list execution
Summary:
Use Unwind + ScanAllByLabelPropertyValue logical operator to
accelerate the execution of queries like the following one:
`MATCH (n:Label) WHERE n.property IN [] ...`

Reviewers: llugovic, mferencevic

Reviewed By: llugovic, mferencevic

Subscribers: llugovic, pullbot

Differential Revision: https://phabricator.memgraph.io/D2758
2020-04-21 11:38:38 +02:00
Marko Budiselic
e04b96a0ac Reduce the frame stack size
Reviewers: llugovic, mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2762
2020-04-19 20:27:20 +02:00
Matej Ferencevic
21be41995c Fix CMake RPM typo and specify all package config files
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2757
2020-04-06 19:12:30 +02:00
Matej Ferencevic
6c69351ea1 Fix RPM packaging
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2756
2020-04-06 16:01:48 +02:00
Matej Ferencevic
e9ea8693fa Fix CentOS compilation
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2755
2020-04-06 16:01:35 +02:00
Matej Ferencevic
9d3f75a34d Don't link with libreadline in community release
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2754
2020-04-06 10:17:02 +02:00
Marko Budiselic
590fc5c9a8 Add NetworkX to Dockerfiles
Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2753
2020-04-04 15:41:59 +02:00
Matej Ferencevic
1d5952bb9c Fix RPM package installation
Summary: `chattr` must be after `chown`.

Reviewers: buda

Reviewed By: buda

Differential Revision: https://phabricator.memgraph.io/D2752
2020-04-03 18:50:52 +02:00
Matej Ferencevic
24eb021840 Fix wrong function invocation in get_version script
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2751
2020-04-03 17:38:12 +02:00
Matej Ferencevic
ac3560ec8c Add changelog for version 1.0
Reviewers: buda

Reviewed By: buda

Differential Revision: https://phabricator.memgraph.io/D2750
2020-04-03 14:09:02 +02:00
Matej Ferencevic
974ce1e9e9 Fix Debian package installation
Summary: `chattr` must be after `chown`.

Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2748
2020-04-02 19:46:28 +02:00
Matej Ferencevic
66e644551f Clean-up packaged examples
Summary:
The snapshot files now have identical names each time they are generated. Also,
all unnecessary directories (like `auth`) are removed from the examples
automatically.

Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2747
2020-04-02 19:45:32 +02:00
Matej Ferencevic
df72723664 Install Python query modules
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2749
2020-04-02 19:45:12 +02:00
Dino Santl
96bb7a87e3 Add graph analyzer python module.
Summary:
In new commit time limit is introduced.

Also, subgraph query is a little bit different because of memory leak. If we use subgraph in a same way as in wcc.py module then we cannot have Exceptions - reason is memory leak in parameters (node and edge list). Edge and node lists are now list of integers (database id).

P.S. if you know how avoid this please let me know.

Reviewers: mferencevic, llugovic, tsabolcec, tlastre, buda

Reviewed By: mferencevic, llugovic, tlastre, buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2736
2020-04-02 15:26:20 +02:00
Matej Ferencevic
39deba3b9e Fix wrong implicit cast in query dumper
Summary: All integers are signed in openCypher.

Reviewers: buda, llugovic

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2746
2020-04-01 18:35:23 +02:00
Matej Ferencevic
d04993df67 Improve storage unit test
Summary: The tests are extracted from D2570.

Reviewers: llugovic, buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2745
2020-04-01 15:26:44 +02:00
Matej Ferencevic
7e35798401 Remove leftover raft unit test
Reviewers: llugovic, buda

Reviewed By: llugovic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2744
2020-04-01 13:48:02 +02:00
Matej Ferencevic
1d2bb2cda6 Remove leftover traces of HA from query
Reviewers: llugovic

Reviewed By: llugovic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2743
2020-04-01 13:47:47 +02:00
Lovro Lugovic
8fb3a53b78 Expose query timeout checking to modules
Summary:
- Add the `mgp_must_abort(const mgp_graph *graph)` C API.
- Add the `ProcCtx.must_abort()` Python API.

The usage is very simple -- the function returns a boolean indicating whether
the procedure should abort.

Reviewers: mferencevic, dsantl

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2742
2020-04-01 13:30:03 +02:00
Matej Ferencevic
a1b5bdd88f Implement concurrent tests for storage indices
Summary: The same test is written both for label indices and label+property indices.

Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2741
2020-04-01 10:43:05 +02:00
Matej Ferencevic
3cd89e1fe4 Remove internal mgp.py file from Python traceback
Reviewers: llugovic

Reviewed By: llugovic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2740
2020-03-31 15:49:06 +02:00
Matej Ferencevic
90c83de7c2 Fix Python memory leaks when exceptions are thrown
Summary:
The Python `ExceptionInfo` data has the potential to extend lifetime of Python
objects that were on the Python frame while the execution was thrown. This
lifetime extension is very dangerous when executing Python procedures because
we use a custom internal memory allocator that is destroyed immediately when
the procedure is done with its execution.

This diff only keeps the formatted Python traceback (as a string) to avoid any
lifetime extension for the Python objects so that the memory allocator can
safely be destroyed after the procedure has finished its execution.

Reviewers: llugovic

Reviewed By: llugovic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2739
2020-03-31 14:24:05 +02:00
Matej Ferencevic
7cd96dc2f5 Fix Python objects init
Summary:
`PyObject_Init` shouldn't be used in conjunction with `PyObject_New`.

The official Python documentation doesn't say this explicitly and many examples
on the web suggest that they must be used together. When they are used together
everything works when Memgraph is built against the release Python binary, but
doesn't work when built against the debug Python binary.

An implicit example that `PyObject_Init` shouldn't be used with `PyObject_New`
can be found in the Python source, in the file `Include/objimpl.h`:
```
   This example code implements an object constructor with a custom
   allocator, where PyObject_New is inlined, and shows the important
   distinction between two steps (at least):
       1) the actual allocation of the object storage;
       2) the initialization of the Python specific fields
      in this storage with PyObject_{Init, InitVar}.

   PyObject *
   YourObject_New(...)
   {
       PyObject *op;

       op = (PyObject *) Your_Allocator(_PyObject_SIZE(YourTypeStruct));
       if (op == NULL)
       return PyErr_NoMemory();

       PyObject_Init(op, &YourTypeStruct);

       op->ob_field = value;
       ...
       return op;
   }

   Note that in C++, the use of the new operator usually implies that
   the 1st step is performed automatically for you, so in a C++ class
   constructor you would start directly with PyObject_Init/InitVar
```
It explains that `PyObject_New` is actually equal to `malloc` +
`PyObject_Init`.

Reviewers: llugovic

Reviewed By: llugovic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2737
2020-03-30 12:40:42 +02:00
Matej Ferencevic
41551bcd36 Fix Python struct formatting
Reviewers: llugovic

Reviewed By: llugovic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2738
2020-03-28 20:13:02 +01:00
Lovro Lugovic
e5b3414335 Implement the PageRank module
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2730
2020-03-25 13:58:50 +01:00
Teon Banek
b7738c64b3 Make py::Object conversion to PyObject * explicit
Summary:
This fixes an issue in Py(Vertex|Edge)GetProperty and prevents any
further issues of that type at the cost of additional typing effort.

Reviewers: ipaljak, llugovic

Reviewed By: ipaljak, llugovic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2735
2020-03-24 12:23:13 +01:00
Matej Ferencevic
a8b81fdcde Unify C/Python query module logging
Summary:
Make logging for operations with C/Python query modules (loading, closing,
reloading) consistent.

Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2734
2020-03-20 16:02:29 +01:00
Matej Ferencevic
9f006c1b57 Fix storage GC indices/constraints subtle race condition
Summary:
In order for this race condition to cause damage, an index/constraint must be
created/dropped at the exact moment that the GC is cleaning
indices/constraints.

Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2733
2020-03-20 12:41:20 +01:00
Teon Banek
2b8f068ca9 Correctly delimit arguments when printing signature
Reviewers: mferencevic, ipaljak, llugovic

Reviewed By: mferencevic, ipaljak

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2732
2020-03-19 14:19:53 +01:00
Teon Banek
d4c2798551 Save and restore the exception in WithMgpModule
Reviewers: mferencevic, ipaljak, llugovic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2731
2020-03-19 14:19:50 +01:00
Marko Budiselic
4b5068e455 Update release docker
Summary:
* Update dockerfiles (add `libseccomp2` `python3`).
* Parse docker offering info from the package name.

Reviewers: teon.banek, mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1924
2020-03-18 17:50:20 +01:00
Ivan Paljak
44917fdfa6 Use storage::EdgeAccessor::Gid() as __hash__ for mgp.Edge
Reviewers: teon.banek, mferencevic

Reviewed By: teon.banek, mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2729
2020-03-18 15:13:13 +01:00
Teon Banek
ba55372610 Fix memory leak in GetVertexById
Reviewers: mferencevic, ipaljak, llugovic

Reviewed By: ipaljak, llugovic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2727
2020-03-18 10:32:19 +01:00
Teon Banek
008efaf243 Implement pretty printing CallProcedure operator
Reviewers: mferencevic, llugovic, ipaljak

Reviewed By: mferencevic, llugovic, ipaljak

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2726
2020-03-18 10:32:16 +01:00
Ivan Paljak
68a1a2da23 Implement wcc using networkx on a given subgraph
Reviewers: buda, teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2723
2020-03-17 14:33:25 +01:00
Ivan Paljak
7b824ed622 Add __hash__ to mgp.Vertex and mgp.Edge
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2725
2020-03-17 13:46:09 +01:00
Matej Ferencevic
5632852890 Reduce number of errors in production log
Summary:
Currently, when starting Memgraph with the production package (DEB/RPM),
Memgraph always outputs an error for not being able to replace an existing
query module (`example.so` with `example.c`). This diff introduces a precheck
so that the error message is correct - so that Memgraph doesn't try to replace
an `.so` file with a `.c` file before verifying that the `.c` file is a valid
query module (which it obviously isn't). Also, I have moved the source of the
example into a subdirectory so that it isn't even considered while loading
modules.

Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2724
2020-03-17 13:34:11 +01:00
Teon Banek
d63eb191f9 Fix a planning issue when CALL preceded filtering
Reviewers: mferencevic, ipaljak, llugovic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2722
2020-03-16 15:06:39 +01:00
Matej Ferencevic
f1327c52ef Fix ownership of examples in packages
Reviewers: teon.banek, ipaljak

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2721
2020-03-13 19:27:16 +01:00
Matej Ferencevic
f48ad62647 Implement storage lock
Summary:
The storage now uses a file in the data directory (`.lock`) to determine
whether there is another instance of the storage running with the same data
directory. That helps notify the user/administrator that the system is running
in an unsupported configuration.

Reviewers: teon.banek, ipaljak

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2719
2020-03-13 19:27:01 +01:00
Teon Banek
eaabcd8d0d Use fully_qualified_name for typing resolution
Reviewers: ipaljak, buda

Reviewed By: buda

Differential Revision: https://phabricator.memgraph.io/D2720
2020-03-12 14:04:44 +01:00
Teon Banek
fb4ce6c5ed Update the example.py to use more API
Reviewers: ipaljak, dsantl, buda, tlastre

Reviewed By: ipaljak, buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2718
2020-03-12 14:04:40 +01:00
Lovro Lugovic
dd5d29fa1d Implement conversion of mgp_vertex to/from mgp.Vertex
Summary:
- Fix AssertPickleAndCopyAreNotSupported
- Test Vertex and Edge conversions

Reviewers: teon.banek, ipaljak

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2717
2020-03-11 16:13:24 +01:00
Teon Banek
963d779d48 Install and setup loading Python Query Modules
Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot, buda

Differential Revision: https://phabricator.memgraph.io/D2715
2020-03-11 10:24:53 +01:00
Matej Ferencevic
5695774251 Improve CSV importer
Summary:
The importer now supports all of the flags that the modern Neo4j CSV importer
supports.

Reviewers: teon.banek, ipaljak

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2709
2020-03-10 15:51:29 +01:00
Ivan Paljak
181f937c15 Add Python class for mgp_properties_iterator
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2712
2020-03-10 14:34:32 +01:00
Tonko Sabolcec
7ea2d1b638 Add concurrent test for unique constraints
Summary:
This diff contains simple tests for unique constraints which tries to
change property values or labels in multiple threads at the same time.
During testing, a bug has been encountered in unique constraints, i.e.
one guard lock on vertices was missing.

Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: mferencevic, pullbot

Differential Revision: https://phabricator.memgraph.io/D2711
2020-03-10 12:52:58 +01:00
Teon Banek
c4a1a6c0b4 Extract py::AppendToSysPath function
Reviewers: mferencevic, ipaljak, llugovic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2714
2020-03-10 11:06:21 +01:00
Teon Banek
d0411510fa Add GetExecutablePath to utils/file
Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2713
2020-03-10 11:06:18 +01:00
Teon Banek
fbdcad1106 Implement calling Python procedures
Summary:
You should now be able to invoke query procedures written in Python. To
test the example you can run memgraph with PYTHONPATH set to `include`.
For example, assuming you are in the root of the repo, run this command.

    PYTHONPATH=$PWD/include ./build/memgraph --query-modules-directory=./query_modules/

Alternatively, you can set a symlink inside the ./query_modules to point
to `include/mgp.py`, so there's no need to set PYTHONPATH. For example,
assuming you are in the root of the repo, run the following.

    cd ./query_modules
    ln -s ../include/mgp.py
    cd ..
    ./build/memgraph --query-modules-directory=./query_modules/

Depends on D207

Reviewers: mferencevic, ipaljak, dsantl

Reviewed By: ipaljak

Subscribers: buda, tlastre, pullbot

Differential Revision: https://phabricator.memgraph.io/D2708
2020-03-06 16:25:51 +01:00
Tonko Sabolcec
6f83fff171 Implement durability functionality for unique constraints
Summary:
This diff contains a necessary functionality to save and restore unique
constraint operations. The previous snapshot/WAL version is backward
compatible. Integration tests for migration from older snapshot and WAL
versions are also included.

Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2680
2020-03-06 14:14:54 +01:00
Matej Ferencevic
8363991575 Implement new CSV parser
Summary:
The new CSV parser in `mg_import_csv` behaves the same when importing a CSV
file as the standard Python CSV importer. Tests are added for all CSV field
edge-cases.

Reviewers: teon.banek, ipaljak

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2702
2020-03-06 11:32:54 +01:00
Teon Banek
1d30a2e5cb Convert mgp_value to 'mgp' Python types, not '_mgp' ones
Summary:
When invoking a Python registered procedures we want to convert
`mgp_value` types to user facing 'mgp' types.

Depends on D2706

Reviewers: mferencevic, ipaljak

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2707
2020-03-06 11:23:08 +01:00
Teon Banek
31a4c55e76 Add HasAttr, GetAttr, and SetAttr to py::Object
Reviewers: ipaljak

Reviewed By: ipaljak

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2706
2020-03-06 11:22:39 +01:00
Teon Banek
456267249e Convert mgp_list to PyTuple instead of PyList
Summary: It's much safer to give users immutable types.

Reviewers: ipaljak

Reviewed By: ipaljak

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2705
2020-03-06 11:22:18 +01:00
Marko Budiselic
32b35f6f88 Clean docs folder
Summary:
Removed folders:
  * dev/diagram
  * dev/distributed
  * feature_ref

Reviewers: teon.banek, mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2710
2020-03-05 18:32:14 +01:00
Matej Ferencevic
000d6dba55 Fix utils::SkipList const iterator bug
Summary:
This issue was already fixed in D2119 for the iterator, but I missed to fix the
const iterator in that diff...

Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2703
2020-03-05 11:17:26 +01:00
Teon Banek
6e9404c89a Correctly implement mgp_value move constructor
Reviewers: mferencevic, ipaljak

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2704
2020-03-05 10:00:52 +01:00
Teon Banek
d531e44bef Merge Invalid.*Error into InvalidContextError
Summary:
There's no need for so many Exception subclasses, because the root cause
is always the same.

Depends on D2700

Reviewers: mferencevic, ipaljak

Reviewed By: mferencevic, ipaljak

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2701
2020-03-04 12:52:05 +01:00
Teon Banek
74d9dd0b0a Implement mgp.Path
Reviewers: ipaljak, mferencevic

Reviewed By: ipaljak, mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2700
2020-03-04 12:51:06 +01:00
Matej Ferencevic
a456d6cdc0 Implement mg_import_csv
Summary:
This diff restores (and fixes) the old mg_import_csv implementation. The
importer now supports the new storage engine.

Reviewers: teon.banek, ipaljak

Reviewed By: teon.banek, ipaljak

Subscribers: buda, pullbot

Differential Revision: https://phabricator.memgraph.io/D2690
2020-03-03 14:33:43 +01:00
Ivan Paljak
ec0caad44c Fix memory leaks in Py*Iter* methods
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2699
2020-03-02 14:40:59 +01:00
Tonko Sabolcec
14c4c6d060 Update query dump with unique constraints
Summary:
Now that unique constraint feature is added to Memgraph database,
we should update `DUMP DATABASE` with list of existing unique constraints.

Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2698
2020-03-02 13:50:35 +01:00
Teon Banek
b922ca75d0 Add mgp_path_equal to C API
Reviewers: ipaljak, mferencevic

Reviewed By: ipaljak

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2697
2020-03-02 13:19:22 +01:00
Teon Banek
f5a94d6e29 Make MakePy<name> less error prone
Reviewers: ipaljak

Reviewed By: ipaljak

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2696
2020-03-02 13:19:19 +01:00
Tonko Sabolcec
eed83a210e Implement unique constraint functionality in query module
Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2681
2020-03-02 13:00:04 +01:00
Matej Ferencevic
0e95934719 Remove leftover implementation required by the old storage
Reviewers: teon.banek, ipaljak

Reviewed By: teon.banek, ipaljak

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2687
2020-03-02 12:45:18 +01:00
Teon Banek
565927631f Update docs/dev/query/planning.md
Reviewers: buda

Reviewed By: buda

Differential Revision: https://phabricator.memgraph.io/D2691
2020-03-02 09:07:25 +01:00
Teon Banek
64da28ca83 Fix some memory leaks in PyObjectToMgpValue
Reviewers: ipaljak

Reviewed By: ipaljak

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2695
2020-02-28 10:57:39 +01:00
Teon Banek
4049b9d10a Add mgp_path_copy to C API
Reviewers: ipaljak

Reviewed By: ipaljak

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2694
2020-02-28 10:57:36 +01:00
Ivan Paljak
bb4e747fd3 Finalize PyEdgeFromVertex and PyEdgeToVertex implementations
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2693
2020-02-28 09:36:47 +01:00
Ivan Paljak
b6b6403b06 Implement PyEdgesIterator and integrate with PyVertex
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2692
2020-02-27 15:57:02 +01:00
Teon Banek
ad892f2db3 Support typing annotations in Python Query Modules
Reviewers: mferencevic, ipaljak, tlastre

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2689
2020-02-27 13:18:57 +01:00
Ivan Paljak
32e56684db Add Python class for mgp_vertex
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2688
2020-02-26 14:01:43 +01:00
Matej Ferencevic
bfbace8168 Remove old HA implementation
Reviewers: teon.banek, ipaljak

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2686
2020-02-25 17:19:35 +01:00
Matej Ferencevic
4e5a91e7fb Merge memgraph_init with main file
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2684
2020-02-25 12:25:25 +01:00
Matej Ferencevic
2562070b06 Use std::string_view in storage NameTo... functions
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2683
2020-02-25 12:15:31 +01:00
Teon Banek
e7f363ecbe Add Python class for mgp_edge
Reviewers: teon.banek, ipaljak, mferencevic

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2679
2020-02-25 10:26:08 +01:00
Teon Banek
34db077cbd Register Python procedures
Summary:
With this diff you should now be able to register `example.py` and read
procedures found there. The procedures will be listed through `CALL
mg.procedures() YIELD *` query, but invoking them will raise
`NotYetImplemented`.

If you wish to test this, you will need to run the Memgraph executable
with PYTHONPATH set to the `include` directory where `mgp.py` is found.
Additionally, you need to pass `--query-modules-directory` flag to
Memgraph, such that it points to where it will find the `example.py`.

For example, when running from the root directory of Memgraph repo, the
shell invocation below should do the trick (assuming `./build/memgraph`
is where is the executable). Make sure that `./query_modules/` does not
have `example.so` built, as that may interfere with loading
`example.py`.

    PYTHONPATH=$PWD/include ./build/memgraph --query-modules-directory=./query_modules/

Reviewers: mferencevic, ipaljak, llugovic

Reviewed By: mferencevic, ipaljak, llugovic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2678
2020-02-19 14:35:22 +01:00
Matej Ferencevic
47ce444c02 Fix auth Cypher visitor
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2675
2020-02-17 17:41:08 +01:00
Matej Ferencevic
e8903ef477 Expose auth user/role name regex
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2673
2020-02-17 17:40:53 +01:00
Matej Ferencevic
d1be5ea136 Remove cppcheck from arc linters
Summary: Unfortunately, `cppcheck` fails to detect various macros as valid syntax.

Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2674
2020-02-17 17:40:39 +01:00
Matej Ferencevic
c50b518fad Update community license to match the enterprise trial license
Summary:
This diff just changes minor style and removes the `early access` wording from
the license.

Reviewers: buda

Reviewed By: buda

Differential Revision: https://phabricator.memgraph.io/D2672
2020-02-17 14:57:14 +01:00
Matej Ferencevic
dfc546183d Add community to Apollo build
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2671
2020-02-17 13:27:20 +01:00
Lovro Lugovic
5953f07be3 Add conversion of py::Object to mgp_value
Reviewers: teon.banek, ipaljak, mferencevic

Reviewed By: teon.banek, ipaljak

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2667
2020-02-14 16:08:20 +01:00
Teon Banek
ad740e4ae2 Add _mgp.Graph and _mgp.VerticesIterator to embedded Python
Reviewers: llugovic, ipaljak, mferencevic

Reviewed By: ipaljak

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2669
2020-02-14 10:00:55 +01:00
Matej Ferencevic
bd0fd2619c Add cppcheck and clang-format arc linters
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2670
2020-02-13 13:38:49 +01:00
Tonko Sabolcec
95c9755e13 Support property set for unique constraints
Summary:
Before this change, unique constraints supported only pairs of label
and a single property. With this change, unique constraints can be
created for label and set of properties.
Better tests for unique constraints in general are also included in
this diff.

Reviewers: mferencevic, teon.banek

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2653
2020-02-13 12:41:35 +01:00
Matej Ferencevic
4669e0ae7d Enable code coverage
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2666
2020-02-13 11:15:45 +01:00
Teon Banek
6a7b983a58 Normalize the exception in py::FetchError
Reviewers: llugovic, ipaljak

Reviewed By: llugovic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2668
2020-02-12 16:32:24 +01:00
Matej Ferencevic
beec3e3f5f Set RPM package license type
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2663
2020-02-12 13:28:26 +01:00
Matej Ferencevic
ffbe5b449d Implement version names for each Memgraph version
Summary:
With this diff, each build of Memgraph has a version that uniquely identifies
it. The given version uniquely identifies both official release builds and
development builds. Enterprise/community builds are also differentiated in the
version.  Also, support for custom suffixes is added to support custom builds
for customers.

Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2662
2020-02-12 10:05:40 +01:00
Teon Banek
0a7de969f3 Add converting mgp_value to py::Object
Reviewers: mferencevic, ipaljak, llugovic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2665
2020-02-12 09:30:14 +01:00
Ivan Paljak
2aa960403a Refactor query module to support differently loaded modules
Summary:
`query::procedure::Module` is now an interface class through which
users interact with loaded modules. At the moment, the interface is implemented
by three concrete classes: `query::procedure::BuiltinModule`,
`query::procedure::SharedLibraryModule` and `query::procedure::PythonModule`.

Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2664
2020-02-11 16:56:26 +01:00
Teon Banek
d17d1497d1 Add missing includes to py.hpp
Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2661
2020-02-10 14:37:05 +01:00
Tonko Sabolcec
e2e7823ec4 Add basic support for unique constraints in storage
Summary:
This diff contains a basic implementation of unique constraints consistent with
the MVCC storage.
Stale records in the unique constraints are collected by the garbage collector.
Tests for checking correctness of unique constraints and violations are included.
Note: currently we only support a pair of label and a single property. Support for
multiple properties will be added later.

Reviewers: mferencevic, teon.banek

Reviewed By: mferencevic, teon.banek

Subscribers: buda, ipaljak, pullbot

Differential Revision: https://phabricator.memgraph.io/D2608
2020-02-10 12:45:28 +01:00
Teon Banek
ab53cbb0c3 Embed Python and import modules into global interpreter
Summary:
This only imports the `.py` files into the global interpreter. These
modules are not exposed to query execution. A later diff will add that
support.

Reviewers: mferencevic, ipaljak

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2660
2020-02-10 11:06:08 +01:00
Matej Ferencevic
90286b8166 Install license, systemd and examples from release
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2659
2020-02-07 10:54:07 +01:00
Matej Ferencevic
7128320b36 Implement MG_ENTERPRISE CMake flag
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2658
2020-02-06 13:39:30 +01:00
Matej Ferencevic
87511460f1 Remove unused CMake options
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2656
2020-02-05 14:52:22 +01:00
Matej Ferencevic
3e20afde6e Increase stress test query timeout
Reviewers: teon.banek, ipaljak

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2657
2020-02-05 14:52:13 +01:00
Matej Ferencevic
29aa9b9e92 Move CPack setup to release
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2655
2020-02-05 10:26:41 +01:00
Teon Banek
4281a57923 Stub out full Python API for Query Modules
Summary:
This is the Python 3.5+ version of our C API found in
include/mg_procedure.h. It should strive to be simple to use, but very
hard to misuse. The Python API avoids almost all memory management
issues found in the C API, so it is automatically less error prone.

Unfortunately, we cannot avoid the issue of having some objects being
alive only during the execution of a custom procedure. For such objects
we have a `Invalid<Object>Error` exception types. The Python API should
fairly easily check the validity because all such objects are related to
ProcCtx in some way. Our C/C++ part will push 1 instance of ProcCtx each
time a Python procedure is invoked and can therefore easily change
ProcCtx to be invalid. This can be as simple as changing the
implementation pointer of ProcCtx to NULL.

Reviewers: mferencevic, ipaljak, tlastre, dsantl, buda

Reviewed By: mferencevic, tlastre

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2650
2020-02-04 13:10:10 +01:00
Matej Ferencevic
6c422f3208 Remove global include directories in CMake
Summary:
All external libraries now automatically include their include directories. It
is necessary only to link to the external library using
`target_link_libraries(target library)` and the include directory of the
library will be automatically available for the binary.

Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2654
2020-02-03 13:18:30 +01:00
Matej Ferencevic
a3229881bf Move Antlr generation to query
Summary:
The antlr openCypher parser generation is moved to query. Also, header files
have been added to the list of generated files so that if any header file is
deleted CMake will know that it has to regenerate it.

Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2652
2020-01-31 16:14:05 +01:00
Matej Ferencevic
c96421424f Reorganize unit CMakeLists
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2651
2020-01-31 11:18:36 +01:00
Matej Ferencevic
d156d5b41c Remove customers/experimental/poc
Reviewers: teon.banek, buda

Reviewed By: teon.banek, buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2648
2020-01-29 16:59:45 +01:00
Matej Ferencevic
84a6ab75cb Fix race condition in long running stress test
Summary:
The long running stress test had a subtle race condition which caused the test
to fail with an error message like "Runner X edges creation failed because of:
Can't serialize due to concurrent operations.". This situation was caused
because some workers could complete their initialization (initial vertex and
edge creation) before other workers. The workers that completed their
initialization would then proceed to execute the test. In the test they could
execute queries that make global updates on the graph that could interfere with
the concurrently running initialization queries of other workers.

This diff makes the runners wait until all initialization queries are fully
executed before they execute global operations on the graph.

Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2647
2020-01-29 10:19:53 +01:00
Matej Ferencevic
fd87facf65 Make audit a standalone library
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2649
2020-01-28 16:37:07 +01:00
Teon Banek
ec4d8c7bb1 Write query_modules/example.c as example.py
Summary:
This is an overview on how the example Query Module will look in
Python.

Reviewers: mferencevic, ipaljak, buda, tlastre, dsantl

Reviewed By: ipaljak, tlastre, dsantl

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2642
2020-01-28 14:29:50 +01:00
Matej Ferencevic
cfbfeb1dfc Remove TotalOrdering from utils::Timestamp
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2646
2020-01-28 10:40:55 +01:00
Matej Ferencevic
015573b5af Clean-up utils/hashing
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2645
2020-01-28 10:40:43 +01:00
Matej Ferencevic
de48548164 Move RPC to root source directory
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2641
2020-01-27 11:50:24 +01:00
Matej Ferencevic
7d0590a753 Remove wrongly committed TODO
Summary: Sorry, won't happen again...

Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2643
2020-01-27 11:49:59 +01:00
Matej Ferencevic
fd81ebdfe3 Move KVStore to root source directory
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2640
2020-01-24 16:01:51 +01:00
Matej Ferencevic
0c7313bb5f Remove unused datastructures
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2639
2020-01-24 15:11:02 +01:00
Matej Ferencevic
c425624ba6 Remove old single node storage implementation
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2638
2020-01-24 15:10:41 +01:00
Matej Ferencevic
0683ed4134 Remove MG_SINGLE_NODE_V2 define
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2637
2020-01-23 17:24:33 +01:00
Matej Ferencevic
6d10d90d98 Remove unused utils
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2636
2020-01-23 17:22:51 +01:00
Teon Banek
4f16b814c7 Use ExpandVariable to existing when possible
Reviewers: mferencevic, ipaljak

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2634
2020-01-23 17:12:24 +01:00
Teon Banek
029a36eab1 Add a feature spec for Python Query Modules
Reviewers: mferencevic, buda, ipaljak, dsantl, tlastre

Reviewed By: mferencevic, buda, ipaljak, dsantl

Differential Revision: https://phabricator.memgraph.io/D2629
2020-01-23 17:10:23 +01:00
Matej Ferencevic
a1fa7de115 Remove leftover old storage types
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2635
2020-01-23 16:24:56 +01:00
Matej Ferencevic
98dc7e2849 Remove old storage types
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2633
2020-01-23 14:32:33 +01:00
Ivan Paljak
1a50165c22 Remove log compaction from current HA implementation
Reviewers: mferencevic, buda

Reviewed By: mferencevic, buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2632
2020-01-23 10:38:37 +01:00
Teon Banek
c11d391e62 Print the type of ExpandVariable in EXPLAIN
Reviewers: mferencevic, ipaljak

Reviewed By: mferencevic, ipaljak

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2631
2020-01-22 16:28:18 +01:00
Matej Ferencevic
b7a5532cc2 Add server name to Bolt
Reviewers: buda, teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2021
2020-01-21 12:03:16 +01:00
Matej Ferencevic
1fb5d14751 Improve auth module error handling and support relative paths
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2628
2020-01-21 11:02:14 +01:00
Ivan Paljak
3121f7d89d Update HA feature spec
Reviewers: buda, teon.banek, mferencevic

Reviewed By: buda

Subscribers: dsantl, dlozic, tlastre

Differential Revision: https://phabricator.memgraph.io/D2626
2020-01-20 17:55:25 +01:00
Ivan Paljak
3a88668ace Output Memgraph version to stdout on startup
Reviewers: teon.banek, mferencevic

Reviewed By: teon.banek

Differential Revision: https://phabricator.memgraph.io/D2627
2020-01-17 13:04:05 +01:00
Teon Banek
cc2160d397 Remove linking mg-query with mg-auth
Summary: Depends on D2623

Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2624
2020-01-16 17:02:37 +01:00
Teon Banek
c7f69fd861 Extract AuthQuery handling to an interface class
Reviewers: mferencevic, llugovic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2623
2020-01-16 17:02:35 +01:00
Teon Banek
5fefd9d82f Document throwing methods in Auth, User and Role classes
Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2622
2020-01-16 16:08:59 +01:00
Matej Ferencevic
c3bfd3004b Reduce memory allocations in indices
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2621
2020-01-15 14:52:26 +01:00
Teon Banek
9889b9421d tests/unit: Don't ignore result of DetachRemoveVertex
Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2620
2020-01-14 13:00:08 +01:00
Matej Ferencevic
4776bea221 Temporarily disable HA build and tests
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2619
2020-01-13 17:04:27 +01:00
Matej Ferencevic
5906258de0 Stop building storage v1
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2618
2020-01-13 14:18:12 +01:00
Matej Ferencevic
79947c376b Migrate property_based tests to storage v2
Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2617
2020-01-13 13:09:57 +01:00
Teon Banek
0c7f384fd2 Make Vertex|Edge lock member mutable
Reviewers: mferencevic, ipaljak

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2614
2020-01-13 10:10:02 +01:00
Teon Banek
2011aea165 Remove mg-single-node-v2 library
Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2613
2020-01-10 09:58:33 +01:00
Teon Banek
ba6632a00d Link benchmarks and manual tests with mg-query
Summary: Depends on D2611

Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2612
2020-01-08 14:33:39 +01:00
Teon Banek
551c6c5ad6 Add mg-query library and use it in unit tests
Reviewers: mferencevic, ipaljak

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2611
2020-01-08 14:33:35 +01:00
Ivan Paljak
3a0dda976a Fix spelling mistakes in v2/durability
Reviewers: buda

Reviewed By: buda

Differential Revision: https://phabricator.memgraph.io/D2610
2019-12-31 15:05:14 +01:00
Matej Ferencevic
b5e255b896 Implement local buffer for PropertyStore
Reviewers: teon.banek, ipaljak

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2605
2019-12-23 15:34:51 +01:00
Matej Ferencevic
b3d1cd8257 Improve long running stress test
Summary:
The test is now more strict than before. Each verification step doesn't just
verify that object counts are correct, it now also verifies that all object IDs
are correct. The continuous integration script is improved to have a more
deterministic startup.

Reviewers: teon.banek, ipaljak

Reviewed By: teon.banek, ipaljak

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2606
2019-12-23 14:18:52 +01:00
Matej Ferencevic
d968370c3e Implement property store
Summary:
The property store stores a map of `PropertyId` to `PropertyValue` mappings. It
compresses all of the values in order to use as little memory as possible.

Reviewers: teon.banek, ipaljak

Reviewed By: teon.banek

Subscribers: buda, pullbot

Differential Revision: https://phabricator.memgraph.io/D2604
2019-12-23 13:28:46 +01:00
Teon Banek
d910813955 Optimize vertex lookup by ID
Reviewers: mferencevic, llugovic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2609
2019-12-23 13:27:40 +01:00
Ivan Paljak
591eadad20 Fix modularity calculation bug in Louvain query module
Reviewers: dsantl

Reviewed By: dsantl

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2607
2019-12-18 15:36:43 +01:00
Ivan Paljak
ec67e71d39 Fix bug in modularity calculation
Summary:
This diff does not fix our Louvain implementation completely. The algorithm
internally still optimzies the wrong function, but this will at least correctly
calculate the modularity value at the end.

Fix of the algorithm will come in a separate diff

Reviewers: dsantl

Reviewed By: dsantl

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2603
2019-12-16 16:34:40 +01:00
Teon Banek
0c111d52dc Add syntax for limiting memory of CALL
Reviewers: mferencevic, ipaljak

Reviewed By: ipaljak

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2602
2019-12-16 10:58:28 +01:00
1216 changed files with 40341 additions and 706022 deletions

View File

@@ -1,7 +0,0 @@
{
"project_id" : "memgraph",
"conduit_uri" : "https://phabricator.memgraph.io",
"phabricator_uri" : "https://phabricator.memgraph.io",
"git.default-relative-commit": "origin/master",
"arc.land.onto.default": "master"
}

View File

@@ -1,9 +0,0 @@
{
"linters": {
"clang-tidy": {
"type": "script-and-regex",
"script-and-regex.script": "./tools/arc-clang-tidy",
"script-and-regex.regex": "/^(?P<file>.*):(?P<line>\\d+):(?P<char>\\d+): (?P<severity>warning|error): (?P<message>.*)$/m"
}
}
}

View File

@@ -6,4 +6,5 @@ UseTab: Never
DerivePointerAlignment: false
PointerAlignment: Right
ColumnLimit : 80
IncludeBlocks: Preserve
...

View File

@@ -36,6 +36,9 @@ Checks: '*,
-hicpp-use-equals-default,
-hicpp-vararg,
-llvm-header-guard,
-llvmlibc-callee-namespace,
-llvmlibc-implementation-in-namespace,
-llvmlibc-restrict-system-libc-headers,
-misc-non-private-member-variables-in-classes,
-misc-unused-parameters,
-modernize-avoid-c-arrays,

43
.githooks/pre-commit Executable file
View File

@@ -0,0 +1,43 @@
#!/bin/sh
project_folder=$(git rev-parse --show-toplevel)
if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=$(git hash-object -t tree /dev/null)
fi
# Redirect output to stderr.
exec 1>&2
tmpdir=$(mktemp -d repo-XXXXXXXX)
trap "rm -rf $tmpdir" EXIT INT
modified_files=$(git diff --cached --name-only --diff-filter=AM $against | sed -nE "/.*\.(cpp|cc|cxx|c|h|hpp|lcp)$/p")
for file in $modified_files; do
echo "Checking $file..."
cp $project_folder/.clang-format $project_folder/.clang-tidy $tmpdir
git checkout-index --prefix="$tmpdir/" -- $file
echo "Running clang-format..."
$project_folder/tools/git-clang-format $tmpdir/$file
code=$?
if [ $code -ne 0 ]; then
break
fi
echo "Running clang-tidy..."
$project_folder/tools/git-clang-tidy $tmpdir/$file
code=$?
if [ $code -ne 0 ]; then
break
fi
done;
return $code

330
.github/workflows/diff.yaml vendored Normal file
View File

@@ -0,0 +1,330 @@
name: Diff
on:
push:
paths-ignore:
- 'docs/**'
- '*.md'
jobs:
community_build:
name: "Community build"
runs-on: [self-hosted, General, Linux, X64, Debian10]
env:
THREADS: 24
steps:
- name: Set up repository
uses: actions/checkout@v2
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build community binaries
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Initialize dependencies.
./init
# Build community binaries.
cd build
cmake -DCMAKE_BUILD_TYPE=release -DMG_ENTERPRISE=OFF ..
make -j$THREADS
- name: Run unit tests
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Run unit tests.
cd build
ctest -R memgraph__unit --output-on-failure
- name: Run stress test
run: |
cd tests/stress
./continuous_integration
coverage_build:
name: "Coverage build"
runs-on: [self-hosted, General, Linux, X64, Debian10]
env:
THREADS: 24
steps:
- name: Set up repository
uses: actions/checkout@v2
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build coverage binaries
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Initialize dependencies.
./init
# Build coverage binaries.
cd build
cmake -DTEST_COVERAGE=ON ..
make -j$THREADS memgraph__unit
- name: Run unit tests
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Run unit tests.
cd build
ctest -R memgraph__unit --output-on-failure
- name: Compute code coverage
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Compute code coverage.
cd tools/github
./coverage_convert
# Package code coverage.
cd generated
tar -czf code_coverage.tar.gz coverage.json html report.json summary.rmu
- name: Save code coverage
uses: actions/upload-artifact@v2
with:
name: "Code coverage"
path: tools/github/generated/code_coverage.tar.gz
debug_build:
name: "Debug build"
runs-on: [self-hosted, General, Linux, X64, Debian10]
env:
THREADS: 24
steps:
- name: Set up repository
uses: actions/checkout@v2
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build debug binaries
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Initialize dependencies.
./init
# Build debug binaries.
cd build
cmake ..
make -j$THREADS
- name: Run leftover CTest tests
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Run leftover CTest tests (all except unit and benchmark tests).
cd build
ctest -E "(memgraph__unit|memgraph__benchmark)" --output-on-failure
- name: Run drivers tests
run: |
./tests/drivers/run.sh
- name: Run integration tests
run: |
cd tests/integration
for name in *; do
if [ ! -d $name ]; then continue; fi
pushd $name >/dev/null
echo "Running: $name"
if [ -x prepare.sh ]; then
./prepare.sh
fi
if [ -x runner.py ]; then
./runner.py
elif [ -x runner.sh ]; then
./runner.sh
fi
echo
popd >/dev/null
done
- name: Run cppcheck and clang-format
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Run cppcheck and clang-format.
cd tools/github
./cppcheck_and_clang_format diff
- name: Save cppcheck and clang-format errors
uses: actions/upload-artifact@v2
with:
name: "Code coverage"
path: tools/github/cppcheck_and_clang_format.txt
release_build:
name: "Release build"
runs-on: [self-hosted, General, Linux, X64, Debian10]
env:
THREADS: 24
steps:
- name: Set up repository
uses: actions/checkout@v2
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Set up parent
run: |
# Remove parent folder (if it exists).
cd ..
if [ -d parent ]; then
rm -rf parent
fi
# Copy untouched repository to parent folder.
cp -r memgraph parent
# Checkout previous commit
cd parent
git checkout HEAD~1
- name: Build release binaries
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Initialize dependencies.
./init
# Build release binaries.
cd build
cmake -DCMAKE_BUILD_TYPE=release ..
make -j$THREADS
- name: Build parent binaries
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Initialize dependencies.
cd ../parent
./init
# Build parent binaries.
cd build
cmake -DCMAKE_BUILD_TYPE=release ..
make -j$THREADS memgraph memgraph__macro_benchmark
- name: Run macro benchmark tests
run: |
cd tests/macro_benchmark
./harness QuerySuite MemgraphRunner \
--groups aggregation 1000_create unwind_create dense_expand match \
--no-strict
- name: Run parent macro benchmark tests
run: |
cd ../parent/tests/macro_benchmark
./harness QuerySuite MemgraphRunner \
--groups aggregation 1000_create unwind_create dense_expand match \
--no-strict
- name: Compute macro benchmark summary
run: |
./tools/github/macro_benchmark_summary \
--current tests/macro_benchmark/.harness_summary \
--previous ../parent/tests/macro_benchmark/.harness_summary \
--output macro_benchmark_summary.txt
- name: Save macro benchmark summary
uses: actions/upload-artifact@v2
with:
name: "Macro benchmark summary"
path: macro_benchmark_summary.txt
- name: Run GQL Behave tests
run: |
cd tests/gql_behave
./continuous_integration
- name: Save quality assurance status
uses: actions/upload-artifact@v2
with:
name: "GQL Behave Status"
path: |
tests/gql_behave/gql_behave_status.csv
tests/gql_behave/gql_behave_status.html
- name: Run stress test (plain)
run: |
cd tests/stress
./continuous_integration
- name: Run stress test (SSL)
run: |
cd tests/stress
./continuous_integration --use-ssl
- name: Run durability test
run: |
cd tests/stress
source ve3/bin/activate
python3 durability --num-steps 5
release_jepsen_test:
name: "Release Jepsen Test"
runs-on: [self-hosted, Linux, X64, Debian10, JepsenControl]
#continue-on-error: true
env:
THREADS: 24
steps:
- name: Set up repository
uses: actions/checkout@v2
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build release binaries
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Initialize dependencies.
./init
# Build only memgraph release binarie.
cd build
cmake -DCMAKE_BUILD_TYPE=release ..
make -j$THREADS memgraph
- name: Run Jepsen tests
run: |
cd tests/jepsen
./run.sh test --binary ../../build/memgraph --run-args "test-all --node-configs resources/node-config.edn" --ignore-run-stdout-logs --ignore-run-stderr-logs
- name: Save Jepsen report
uses: actions/upload-artifact@v2
if: ${{ always() }}
with:
name: "Jepsen Report"
path: tests/jepsen/Jepsen.tar.gz

309
.github/workflows/release.yaml vendored Normal file
View File

@@ -0,0 +1,309 @@
name: Release
on:
workflow_dispatch:
schedule:
- cron: "0 1 * * *"
jobs:
community_build:
name: "Community build"
runs-on: [self-hosted, General, Linux, X64, Debian10]
env:
THREADS: 24
timeout-minutes: 960
steps:
- name: Set up repository
uses: actions/checkout@v2
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build community binaries
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Initialize dependencies.
./init
# Build community binaries.
cd build
cmake -DCMAKE_BUILD_TYPE=release -DMG_ENTERPRISE=OFF ..
make -j$THREADS
- name: Create community DEB package
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Create community DEB package.
cd build
mkdir output && cd output
cpack -G DEB --config ../CPackConfig.cmake
- name: Save community DEB package
uses: actions/upload-artifact@v2
with:
name: "Community DEB package"
path: build/output/memgraph*.deb
- name: Run unit tests
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Run unit tests.
cd build
ctest -R memgraph__unit --output-on-failure
- name: Run stress test (plain)
run: |
cd tests/stress
./continuous_integration
- name: Run stress test (large)
run: |
cd tests/stress
./continuous_integration --large-dataset
coverage_build:
name: "Coverage build"
runs-on: [self-hosted, General, Linux, X64, Debian10]
env:
THREADS: 24
steps:
- name: Set up repository
uses: actions/checkout@v2
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build coverage binaries
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Initialize dependencies.
./init
# Build coverage binaries.
cd build
cmake -DTEST_COVERAGE=ON ..
make -j$THREADS memgraph__unit
- name: Run unit tests
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Run unit tests.
cd build
ctest -R memgraph__unit --output-on-failure
- name: Compute code coverage
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Compute code coverage.
cd tools/github
./coverage_convert
# Package code coverage.
cd generated
tar -czf code_coverage.tar.gz coverage.json html report.json summary.rmu
- name: Save code coverage
uses: actions/upload-artifact@v2
with:
name: "Code coverage"
path: tools/github/generated/code_coverage.tar.gz
debug_build:
name: "Debug build"
runs-on: [self-hosted, General, Linux, X64, Debian10]
env:
THREADS: 24
steps:
- name: Set up repository
uses: actions/checkout@v2
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build debug binaries
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Initialize dependencies.
./init
# Build debug binaries.
cd build
cmake ..
make -j$THREADS
- name: Run leftover CTest tests
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Run leftover CTest tests (all except unit and benchmark tests).
cd build
ctest -E "(memgraph__unit|memgraph__benchmark)" --output-on-failure
- name: Run drivers tests
run: |
./tests/drivers/run.sh
- name: Run integration tests
run: |
cd tests/integration
for name in *; do
if [ ! -d $name ]; then continue; fi
pushd $name >/dev/null
echo "Running: $name"
if [ -x prepare.sh ]; then
./prepare.sh
fi
if [ -x runner.py ]; then
./runner.py
elif [ -x runner.sh ]; then
./runner.sh
fi
echo
popd >/dev/null
done
- name: Run cppcheck and clang-format
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Run cppcheck and clang-format.
cd tools/github
./cppcheck_and_clang_format diff
- name: Save cppcheck and clang-format errors
uses: actions/upload-artifact@v2
with:
name: "Code coverage"
path: tools/github/cppcheck_and_clang_format.txt
release_build:
name: "Release build"
runs-on: [self-hosted, General, Linux, X64, Debian10]
env:
THREADS: 24
timeout-minutes: 960
steps:
- name: Set up repository
uses: actions/checkout@v2
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build release binaries
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Initialize dependencies.
./init
# Build release binaries.
cd build
cmake -DCMAKE_BUILD_TYPE=release ..
make -j$THREADS
- name: Create enterprise DEB package
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Create enterprise DEB package.
cd build
mkdir output && cd output
cpack -G DEB --config ../CPackConfig.cmake
- name: Save enterprise DEB package
uses: actions/upload-artifact@v2
with:
name: "Enterprise DEB package"
path: build/output/memgraph*.deb
- name: Run micro benchmark tests
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Run micro benchmark tests.
cd build
# The `eval` benchmark needs a large stack limit.
ulimit -s 262144
ctest -R memgraph__benchmark -V
- name: Run macro benchmark tests
run: |
cd tests/macro_benchmark
./harness QuerySuite MemgraphRunner \
--groups aggregation 1000_create unwind_create dense_expand match \
--no-strict
- name: Run parallel macro benchmark tests
run: |
cd tests/macro_benchmark
./harness QueryParallelSuite MemgraphRunner \
--groups aggregation_parallel create_parallel bfs_parallel \
--num-database-workers 9 --num-clients-workers 30 \
--no-strict
- name: Run GQL Behave tests
run: |
cd tests/gql_behave
./continuous_integration
- name: Save quality assurance status
uses: actions/upload-artifact@v2
with:
name: "GQL Behave Status"
path: |
tests/gql_behave/gql_behave_status.csv
tests/gql_behave/gql_behave_status.html
- name: Run stress test (plain)
run: |
cd tests/stress
./continuous_integration
- name: Run stress test (SSL)
run: |
cd tests/stress
./continuous_integration --use-ssl
- name: Run stress test (large)
run: |
cd tests/stress
./continuous_integration --large-dataset
- name: Run durability test (plain)
run: |
cd tests/stress
source ve3/bin/activate
python3 durability --num-steps 5
- name: Run durability test (large)
run: |
cd tests/stress
source ve3/bin/activate
python3 durability --num-steps 20

308
.github/workflows/release_centos.yaml vendored Normal file
View File

@@ -0,0 +1,308 @@
name: Release CentOS
on: [workflow_dispatch]
jobs:
community_build:
name: "Community build"
runs-on: [self-hosted, General, Linux, X64, CentOS7]
env:
THREADS: 24
timeout-minutes: 960
steps:
- name: Set up repository
uses: actions/checkout@v2
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build community binaries
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Initialize dependencies.
./init
# Build community binaries.
cd build
cmake -DCMAKE_BUILD_TYPE=release -DMG_ENTERPRISE=OFF ..
make -j$THREADS
- name: Create community RPM package
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Create community RPM package.
cd build
mkdir output && cd output
cpack -G RPM --config ../CPackConfig.cmake
rpmlint memgraph*.rpm
- name: Save community RPM package
uses: actions/upload-artifact@v2
with:
name: "Community RPM package"
path: build/output/memgraph*.rpm
- name: Run unit tests
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Run unit tests.
cd build
ctest -R memgraph__unit --output-on-failure
- name: Run stress test (plain)
run: |
cd tests/stress
./continuous_integration
- name: Run stress test (large)
run: |
cd tests/stress
./continuous_integration --large-dataset
coverage_build:
name: "Coverage build"
runs-on: [self-hosted, General, Linux, X64, CentOS7]
env:
THREADS: 24
steps:
- name: Set up repository
uses: actions/checkout@v2
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build coverage binaries
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Initialize dependencies.
./init
# Build coverage binaries.
cd build
cmake -DTEST_COVERAGE=ON ..
make -j$THREADS memgraph__unit
- name: Run unit tests
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Run unit tests.
cd build
ctest -R memgraph__unit --output-on-failure
- name: Compute code coverage
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Compute code coverage.
cd tools/github
./coverage_convert
# Package code coverage.
cd generated
tar -czf code_coverage.tar.gz coverage.json html report.json summary.rmu
- name: Save code coverage
uses: actions/upload-artifact@v2
with:
name: "Code coverage"
path: tools/github/generated/code_coverage.tar.gz
debug_build:
name: "Debug build"
runs-on: [self-hosted, General, Linux, X64, CentOS7]
env:
THREADS: 24
steps:
- name: Set up repository
uses: actions/checkout@v2
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build debug binaries
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Initialize dependencies.
./init
# Build debug binaries.
cd build
cmake ..
make -j$THREADS
- name: Run leftover CTest tests
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Run leftover CTest tests (all except unit and benchmark tests).
cd build
ctest -E "(memgraph__unit|memgraph__benchmark)" --output-on-failure
- name: Run drivers tests
run: |
./tests/drivers/run.sh
- name: Run integration tests
run: |
cd tests/integration
for name in *; do
if [ ! -d $name ]; then continue; fi
pushd $name >/dev/null
echo "Running: $name"
if [ -x prepare.sh ]; then
./prepare.sh
fi
if [ -x runner.py ]; then
./runner.py
elif [ -x runner.sh ]; then
./runner.sh
fi
echo
popd >/dev/null
done
- name: Run cppcheck and clang-format
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Run cppcheck and clang-format.
cd tools/github
./cppcheck_and_clang_format diff
- name: Save cppcheck and clang-format errors
uses: actions/upload-artifact@v2
with:
name: "Code coverage"
path: tools/github/cppcheck_and_clang_format.txt
release_build:
name: "Release build"
runs-on: [self-hosted, General, Linux, X64, CentOS7]
env:
THREADS: 24
timeout-minutes: 960
steps:
- name: Set up repository
uses: actions/checkout@v2
with:
# Number of commits to fetch. `0` indicates all history for all
# branches and tags. (default: 1)
fetch-depth: 0
- name: Build release binaries
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Initialize dependencies.
./init
# Build release binaries.
cd build
cmake -DCMAKE_BUILD_TYPE=release ..
make -j$THREADS
- name: Create enterprise RPM package
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Create enterprise RPM package.
cd build
mkdir output && cd output
cpack -G RPM --config ../CPackConfig.cmake
rpmlint memgraph*.rpm
- name: Save enterprise RPM package
uses: actions/upload-artifact@v2
with:
name: "Enterprise RPM package"
path: build/output/memgraph*.rpm
- name: Run micro benchmark tests
run: |
# Activate toolchain.
source /opt/toolchain-v2/activate
# Run micro benchmark tests.
cd build
# The `eval` benchmark needs a large stack limit.
ulimit -s 262144
ctest -R memgraph__benchmark -V
- name: Run macro benchmark tests
run: |
cd tests/macro_benchmark
./harness QuerySuite MemgraphRunner \
--groups aggregation 1000_create unwind_create dense_expand match \
--no-strict
- name: Run parallel macro benchmark tests
run: |
cd tests/macro_benchmark
./harness QueryParallelSuite MemgraphRunner \
--groups aggregation_parallel create_parallel bfs_parallel \
--num-database-workers 9 --num-clients-workers 30 \
--no-strict
- name: Run GQL Behave tests
run: |
cd tests/gql_behave
./continuous_integration
- name: Save quality assurance status
uses: actions/upload-artifact@v2
with:
name: "GQL Behave Status"
path: |
tests/gql_behave/gql_behave_status.csv
tests/gql_behave/gql_behave_status.html
- name: Run stress test (plain)
run: |
cd tests/stress
./continuous_integration
- name: Run stress test (SSL)
run: |
cd tests/stress
./continuous_integration --use-ssl
- name: Run stress test (large)
run: |
cd tests/stress
./continuous_integration --large-dataset
- name: Run durability test (plain)
run: |
cd tests/stress
source ve3/bin/activate
python3 durability --num-steps 5
- name: Run durability test (large)
run: |
cd tests/stress
source ve3/bin/activate
python3 durability --num-steps 20

118
.github/workflows/test_all_workers.yaml vendored Normal file
View File

@@ -0,0 +1,118 @@
name: Test All Workers
on: [workflow_dispatch]
jobs:
HP-DL360G6-1:
name: "HP-DL360G6-1"
runs-on: [HP-DL360G6-1]
timeout-minutes: 10
steps:
- name: Set up repository
uses: actions/checkout@v2
- name: Check the system
run: |
source /opt/toolchain-v2/activate
./tools/check-build-system
HP-DL360G6-2:
name: "HP-DL360G6-2"
runs-on: [HP-DL360G6-2]
timeout-minutes: 10
steps:
- name: Set up repository
uses: actions/checkout@v2
- name: Check the system
run: |
source /opt/toolchain-v2/activate
./tools/check-build-system
HP-DL360G6-3:
name: "HP-DL360G6-3"
runs-on: [HP-DL360G6-3]
timeout-minutes: 10
steps:
- name: Set up repository
uses: actions/checkout@v2
- name: Check the system
run: |
source /opt/toolchain-v2/activate
./tools/check-build-system
docker --version
docker ps | grep jepsen
HP-DL360G6-v2-1:
name: "HP-DL360G6-v2-1"
runs-on: [HP-DL360G6-v2-1]
timeout-minutes: 10
steps:
- name: Set up repository
uses: actions/checkout@v2
- name: Check the system
run: |
source /opt/toolchain-v2/activate
./tools/check-build-system
HP-DL360G6-v2-2:
name: "HP-DL360G6-v2-2"
runs-on: [HP-DL360G6-v2-2]
timeout-minutes: 10
steps:
- name: Set up repository
uses: actions/checkout@v2
- name: Check the system
run: |
source /opt/toolchain-v2/activate
./tools/check-build-system
HP-DL360G6-v2-3:
name: "HP-DL360G6-v2-3"
runs-on: [HP-DL360G6-v2-3]
timeout-minutes: 10
steps:
- name: Set up repository
uses: actions/checkout@v2
- name: Check the system
run: |
source /opt/toolchain-v2/activate
./tools/check-build-system
HP-DL360G6-v2-4:
name: "HP-DL360G6-v2-4"
runs-on: [HP-DL360G6-v2-4]
timeout-minutes: 10
steps:
- name: Set up repository
uses: actions/checkout@v2
- name: Check the system
run: |
source /opt/toolchain-v2/activate
./tools/check-build-system
HP-DL360G6-v2-5:
name: "HP-DL360G6-v2-5"
runs-on: [HP-DL360G6-v2-5]
timeout-minutes: 10
steps:
- name: Set up repository
uses: actions/checkout@v2
- name: Check the system
run: |
source /opt/toolchain-v2/activate
./tools/check-build-system
HP-DL360G6-v2-6:
name: "HP-DL360G6-v2-6"
runs-on: [HP-DL360G6-v2-6]
timeout-minutes: 10
steps:
- name: Set up repository
uses: actions/checkout@v2
- name: Check the system
run: |
source /opt/toolchain-v2/activate
./tools/check-build-system
HP-DL360G6-v2-7:
name: "HP-DL360G6-v2-7"
runs-on: [HP-DL360G6-v2-7]
timeout-minutes: 10
steps:
- name: Set up repository
uses: actions/checkout@v2
- name: Check the system
run: |
source /opt/toolchain-v2/activate
./tools/check-build-system

View File

@@ -1,172 +0,0 @@
import os
import os.path
import fnmatch
import logging
import ycm_core
BASE_FLAGS = [
'-Wall',
'-Wextra',
'-Werror',
'-Wno-long-long',
'-Wno-variadic-macros',
'-fexceptions',
'-ferror-limit=10000',
'-std=c++1z',
'-xc++',
'-I/usr/lib/',
'-I/usr/include/',
'-I./src',
'-I./include',
'-I./libs/fmt',
'-I./libs/yaml-cpp',
'-I./libs/glog/include',
'-I./libs/googletest/googletest/include',
'-I./libs/googletest/googlemock/include',
'-I./libs/benchmark/include',
'-I./libs/cereal/include',
# We include cppitertools headers directly from libs directory.
'-I./libs',
'-I./libs/rapidcheck/include',
'-I./libs/antlr4/runtime/Cpp/runtime/src',
'-I./libs/gflags/include',
'-I./experimental/distributed/src',
'-I./libs/postgresql/include',
'-I./libs/bzip2',
'-I./libs/zlib',
'-I./libs/rocksdb/include',
'-I./libs/librdkafka/include/librdkafka',
'-I./build/include'
]
SOURCE_EXTENSIONS = [
'.cpp',
'.cxx',
'.cc',
'.c',
'.m',
'.mm'
]
HEADER_EXTENSIONS = [
'.h',
'.hxx',
'.hpp',
'.hh'
]
# set the working directory of YCMD to be this file
os.chdir(os.path.dirname(os.path.realpath(__file__)))
def IsHeaderFile(filename):
extension = os.path.splitext(filename)[1]
return extension in HEADER_EXTENSIONS
def GetCompilationInfoForFile(database, filename):
if IsHeaderFile(filename):
basename = os.path.splitext(filename)[0]
for extension in SOURCE_EXTENSIONS:
replacement_file = basename + extension
if os.path.exists(replacement_file):
compilation_info = database.GetCompilationInfoForFile(replacement_file)
if compilation_info.compiler_flags_:
return compilation_info
return None
return database.GetCompilationInfoForFile(filename)
def FindNearest(path, target):
candidate = os.path.join(path, target)
if(os.path.isfile(candidate) or os.path.isdir(candidate)):
logging.info("Found nearest " + target + " at " + candidate)
return candidate;
else:
parent = os.path.dirname(os.path.abspath(path));
if(parent == path):
raise RuntimeError("Could not find " + target);
return FindNearest(parent, target)
def MakeRelativePathsInFlagsAbsolute(flags, working_directory):
if not working_directory:
return list(flags)
new_flags = []
make_next_absolute = False
path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ]
for flag in flags:
new_flag = flag
if make_next_absolute:
make_next_absolute = False
if not flag.startswith('/'):
new_flag = os.path.join(working_directory, flag)
for path_flag in path_flags:
if flag == path_flag:
make_next_absolute = True
break
if flag.startswith(path_flag):
path = flag[ len(path_flag): ]
new_flag = path_flag + os.path.join(working_directory, path)
break
if new_flag:
new_flags.append(new_flag)
return new_flags
def FlagsForClangComplete(root):
try:
clang_complete_path = FindNearest(root, '.clang_complete')
clang_complete_flags = open(clang_complete_path, 'r').read().splitlines()
return clang_complete_flags
except:
return None
def FlagsForInclude(root):
try:
include_path = FindNearest(root, 'include')
flags = []
for dirroot, dirnames, filenames in os.walk(include_path):
for dir_path in dirnames:
real_path = os.path.join(dirroot, dir_path)
flags = flags + ["-I" + real_path]
return flags
except:
return None
def FlagsForCompilationDatabase(root, filename):
try:
compilation_db_path = FindNearest(root, 'compile_commands.json')
compilation_db_dir = os.path.dirname(compilation_db_path)
logging.info("Set compilation database directory to " + compilation_db_dir)
compilation_db = ycm_core.CompilationDatabase(compilation_db_dir)
if not compilation_db:
logging.info("Compilation database file found but unable to load")
return None
compilation_info = GetCompilationInfoForFile(compilation_db, filename)
if not compilation_info:
logging.info("No compilation info for " + filename + " in compilation database")
return None
return MakeRelativePathsInFlagsAbsolute(
compilation_info.compiler_flags_,
compilation_info.compiler_working_dir_)
except:
return None
def FlagsForFile(filename):
root = os.path.realpath(filename);
compilation_db_flags = FlagsForCompilationDatabase(root, filename)
if compilation_db_flags:
final_flags = compilation_db_flags
else:
final_flags = BASE_FLAGS
clang_flags = FlagsForClangComplete(root)
if clang_flags:
final_flags = final_flags + clang_flags
include_flags = FlagsForInclude(root)
if include_flags:
final_flags = final_flags + include_flags
return {
'flags': final_flags,
'do_cache': True
}

View File

@@ -1,5 +1,129 @@
# Change Log
## Future
### Breaking Changes
* Added extra information in durability files to support replication making
it incompatible with the durability files generated by older versions of
Memgraph. Even though the replication is an Enterprise feature, the files
are compatible with the Community version.
## v1.2.0
### Breaking Changes
* SSL is disabled by default (`--bolt-cert-file` and `--bolt-key-file` are
empty). This change might only affect the client connection configuration.
### Major Features and Improvements
* Added support for Bolt v4.0 and v4.1.
* Added `mgp_networkx.py` as an alternative implementation of NetworkX graph
objects, which is useful to use Memgraph data from NetworkX algorithms
optimally.
* Added `nxalg.py` query module as a proxy to NetworkX algorithms.
* Added plan optimization to use a label-property index where the property is
not null. As a result, the query engine, instead of scanning all elements and
applying the filter, performs a label-property index lookup when possible.
### Bug Fixes and Other Changes
* Fixed Cypher `ID` function `Null` handling. When the `ID` function receives
`Null`, it will also return `Null`.
* Fixed bug that caused random crashes in SSL communication on platforms
that use older versions of OpenSSL (< 1.1) by adding proper multi-threading
handling.
* Fix `DISCARD` message handling. The query is now executed before discarding
the results.
## v1.1.0
### Major Features and Improvements
* Properties in nodes and edges are now stored encoded and compressed. This
change significantly reduces memory usage. Depending on the specific dataset,
total memory usage can be reduced up to 50%.
* Added support for rescanning query modules. Previously, the query modules
directory was scanned only upon startup. Now it is scanned each time the user
requests to load a query module. The functions used to load the query modules
were renamed to `mg.load()` and `mg.load_all()` (from `mg.reload()` and
`mg.reload_all()`).
* Improved execution performance of queries that have an IN list filter by
using label+property indices.
Example: `MATCH (n:Label) WHERE n.property IN [] ...`
* Added support for `ANY` and `NONE` openCypher functions. Previously, only
`ALL` and `SINGLE` functions were implemented.
### Bug Fixes and Other Changes
* Fixed invalid paths returned by variable expansion when the starting node and
destination node used the same symbol.
Example: `MATCH path = (n:Person {name: "John"})-[:KNOWS*]->(n) RETURN path`
* Improved semantics of `ALL` and `SINGLE` functions to be consistent with
openCypher when handling lists with `Null`s.
* `SHOW CONSTRAINT INFO` now returns property names as a list for unique
constraints.
* Escaped label/property/edgetype names in `DUMP DATABASE` to support names
with spaces in them.
* Fixed handling of `DUMP DATABASE` queries in multi-command transactions
(`BEGIN`, ..., `COMMIT`).
* Fixed handling of various query types in explicit transactions. For example,
constraints were allowed to be created in multi-command transactions
(`BEGIN`, ..., `COMMIT`) but that isn't a transactional operation and as such
can't be allowed in multi-command transactions.
* Fixed integer overflow bugs in `COUNT`, `LIMIT` and `SKIP`.
* Fixed integer overflow bugs in weighted shortest path expansions.
* Fixed various other integer overflow bugs in query execution.
* Added Marvel Comic Universe tutorial.
* Added FootballTransfers tutorial.
## v1.0.0
### Major Features and Improvements
* [Enterprise Ed.] Exposed authentication username/rolename regex as a flag
(`--auth-user-or-role-name-regex`).
* [Enterprise Ed.] Improved auth module error handling and added support for
relative paths.
* Added support for Python query modules. This release of Memgraph supports
query modules written using the already existing C API and the new Python
API.
* Added support for unique constraints. The unique constraint is created with a
label and one or more properties.
* Implemented support for importing CSV files (`mg_import_csv`). The importer
is compatible with the Neo4j batch CSV importer.
* Snapshot and write-ahead log format changed (backward compatible with v0.50).
* Vertices looked up by their openCypher ID (`MATCH (n) WHERE ID(n) = ...`)
will now find the node in O(logn) instead of O(n).
* Improved planning of BFS expansion, a faster, specific approach is now
favored instead of a ScanAll+Filter operation.
* Added syntax for limiting memory of `CALL`.
* Exposed server name that should be used for Bolt handshake as flag
(`--bolt-server-name-for-init`).
* Added several more functions to the query module C API.
* Implemented a storage locking mechanism that prevents the user from
concurrently starting two Memgraph instances with the same data directory.
### Bug Fixes and Other Changes
* [Enterprise Ed.] Fixed a bug that crashed the database when granting
privileges to a user.
* [Enterprise Ed.] Improved Louvain algorithm for community detection.
* Type of variable expansion is now printed in `EXPLAIN` (e.g. ExpandVariable,
STShortestPath, BFSExpand, WeightedShortestPath).
* Correctly display `CALL` in `EXPLAIN` output.
* Correctly delimit arguments when printing the signature of a query module.
* Fixed a planning issue when `CALL` preceded filtering.
* Fixed spelling mistakes in the storage durability module.
* Fixed storage GC indices/constraints subtle race condition.
* Reduced memory allocations in storage API and indices.
* Memgraph version is now outputted to `stdout` when Memgraph is started.
* Improved RPM packaging.
* Reduced number of errors reported in production log when loading query
modules.
* Removed `early access` wording from the Community Offering license.
## v0.50.0
### Breaking Changes

View File

@@ -35,16 +35,97 @@ else()
message(FATAL_ERROR "Couldn't find clang and/or clang++!")
endif()
# Get current commit hash.
execute_process(
OUTPUT_VARIABLE COMMIT_HASH
COMMAND git rev-parse --short HEAD
)
string(STRIP ${COMMIT_HASH} COMMIT_HASH)
# -----------------------------------------------------------------------------
project(memgraph VERSION 0.50.0)
project(memgraph)
# For more information about how to release a new version of Memgraph, see
# `release/README.md`.
# Option that is used to specify which version of Memgraph should be built. The
# default is `ON` which causes the build system to build Memgraph Enterprise.
# Memgraph Community is built if explicitly set to `OFF`.
option(MG_ENTERPRISE "Build Memgraph Enterprise Edition" ON)
# Set the current version here to override the automatic version detection. The
# version must be specified as `X.Y.Z`. Primarily used when building new patch
# versions.
set(MEMGRAPH_OVERRIDE_VERSION "")
# Custom suffix that this version should have. The suffix can be any arbitrary
# string. Primarily used when building a version for a specific customer.
set(MEMGRAPH_OVERRIDE_VERSION_SUFFIX "")
# Variables used to generate the versions.
if (MG_ENTERPRISE)
set(get_version_enterprise "--enterprise")
else()
set(get_version_enterprise "")
endif()
set(get_version_script "${CMAKE_SOURCE_DIR}/release/get_version.py")
# Get version that should be used in the binary.
execute_process(
OUTPUT_VARIABLE MEMGRAPH_VERSION
COMMAND "${get_version_script}" ${get_version_enterprise}
"${MEMGRAPH_OVERRIDE_VERSION}"
"${MEMGRAPH_OVERRIDE_VERSION_SUFFIX}"
)
# Get version that should be used in the DEB package.
execute_process(
OUTPUT_VARIABLE MEMGRAPH_VERSION_DEB
COMMAND "${get_version_script}" ${get_version_enterprise}
--variant deb
"${MEMGRAPH_OVERRIDE_VERSION}"
"${MEMGRAPH_OVERRIDE_VERSION_SUFFIX}"
)
# Get version that should be used in the RPM package.
execute_process(
OUTPUT_VARIABLE MEMGRAPH_VERSION_RPM
COMMAND "${get_version_script}" ${get_version_enterprise}
--variant rpm
"${MEMGRAPH_OVERRIDE_VERSION}"
"${MEMGRAPH_OVERRIDE_VERSION_SUFFIX}"
)
# We want the above variables to be updated each time something is committed to
# the repository. That is why we include a dependency on the current git HEAD
# to trigger a new CMake run when the git repository state changes. This is a
# hack, as CMake doesn't have a mechanism to regenerate variables when
# something changes (only files can be regenerated).
# https://cmake.org/pipermail/cmake/2018-October/068389.html
#
# The hack in the above link is nearly correct but it has a fatal flaw. The
# `CMAKE_CONFIGURE_DEPENDS` isn't a `GLOBAL` property, it is instead a
# `DIRECTORY` property and as such must be set in the `DIRECTORY` scope.
# https://cmake.org/cmake/help/v3.14/manual/cmake-properties.7.html
#
# Unlike the above mentioned hack, we don't use the `.git/index` file. That
# file changes on every `git add` (even on `git status`) so it triggers
# unnecessary recalculations of the release version. The release version only
# changes on every `git commit` or `git checkout`. That is why we watch the
# following files for changes:
# - `.git/HEAD` -> changes each time a `git checkout` is issued
# - `.git/refs/heads/...` -> the value in `.git/HEAD` is a branch name (when
# you are on a branch) and you have to monitor the file of the specific
# branch to detect when a `git commit` was issued
# More details about the contents of the `.git` directory and the specific
# files used can be seen here:
# https://git-scm.com/book/en/v2/Git-Internals-Git-References
set(git_directory "${CMAKE_SOURCE_DIR}/.git")
if (EXISTS "${git_directory}")
set_property(DIRECTORY APPEND PROPERTY
CMAKE_CONFIGURE_DEPENDS "${git_directory}/HEAD")
file(STRINGS "${git_directory}/HEAD" git_head_data)
if (git_head_data MATCHES "^ref: ")
string(SUBSTRING "${git_head_data}" 5 -1 git_head_ref)
set_property(DIRECTORY APPEND PROPERTY
CMAKE_CONFIGURE_DEPENDS "${git_directory}/${git_head_ref}")
endif()
endif()
# -----------------------------------------------------------------------------
# setup CMake module path, defines path for include() and find_package()
@@ -68,20 +149,23 @@ add_custom_target(clean_all
# is easier debugging of compilation and linker flags.
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# c99-designator is disabled because of required mixture of designated and
# non-designated initializers in Python Query Module code (`py_module.cpp`).
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall \
-Werror=switch -Werror=switch-bool -Werror=return-type \
-Werror=return-stack-address")
-Werror=return-stack-address \
-Wno-c99-designator")
# Don't omit frame pointer in RelWithDebInfo, for additional callchain debug.
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
"${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fno-omit-frame-pointer")
# Statically link libgcc and libstdc++, the GCC allows this according to:
# https://gcc.gnu.org/onlinedocs/gcc-8.3.0/libstdc++/manual/manual/license.html
# https://gcc.gnu.org/onlinedocs/gcc-10.2.0/libstdc++/manual/manual/license.html
# https://www.gnu.org/licenses/gcc-exception-faq.html
# Last checked for gcc-8.3 which we are using on the build machines.
# Last checked for gcc-10.2 which we are using on the build machines.
# ** If we change versions, recheck this! **
# ** Static linking is allowed only for executables! **
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++")
@@ -107,13 +191,6 @@ else()
set(CMAKE_CXX_FLAGS_DEBUG "-g")
endif()
# ndebug
option(NDEBUG "No debug" OFF)
message(STATUS "NDEBUG: ${NDEBUG} (be careful CMAKE_BUILD_TYPE can also \
append this flag)")
if(NDEBUG)
add_definitions( -DNDEBUG )
endif()
# -----------------------------------------------------------------------------
# default build type is debug
@@ -127,11 +204,6 @@ message(STATUS "CMake build type: ${CMAKE_BUILD_TYPE}")
# threading
find_package(Threads REQUIRED)
# optional Ltalloc
option(USE_LTALLOC "Use Ltalloc instead of default allocator (default OFF). \
Set this to ON to link with Ltalloc." OFF)
# optional readline
option(USE_READLINE "Use GNU Readline library if available (default ON). \
Set this to OFF to prevent linking with Readline even if it is available." ON)
@@ -142,68 +214,16 @@ if (USE_READLINE)
endif()
endif()
# OpenSSL
find_package(OpenSSL REQUIRED)
set(libs_dir ${CMAKE_SOURCE_DIR}/libs)
add_subdirectory(libs EXCLUDE_FROM_ALL)
include_directories(SYSTEM ${GFLAGS_INCLUDE_DIR})
include_directories(SYSTEM ${GLOG_INCLUDE_DIR})
include_directories(SYSTEM ${FMT_INCLUDE_DIR})
include_directories(SYSTEM ${ANTLR4_INCLUDE_DIR})
include_directories(SYSTEM ${BZIP2_INCLUDE_DIR})
include_directories(SYSTEM ${ZLIB_INCLUDE_DIR})
include_directories(SYSTEM ${ROCKSDB_INCLUDE_DIR})
include_directories(SYSTEM ${LIBRDKAFKA_INCLUDE_DIR})
# -----------------------------------------------------------------------------
# openCypher parser -----------------------------------------------------------
set(opencypher_frontend ${CMAKE_SOURCE_DIR}/src/query/frontend/opencypher)
set(opencypher_generated ${opencypher_frontend}/generated)
set(opencypher_lexer_grammar ${opencypher_frontend}/grammar/MemgraphCypherLexer.g4)
set(opencypher_parser_grammar ${opencypher_frontend}/grammar/MemgraphCypher.g4)
# enumerate all files that are generated from antlr
set(antlr_opencypher_generated_src
${opencypher_generated}/MemgraphCypherLexer.cpp
${opencypher_generated}/MemgraphCypher.cpp
${opencypher_generated}/MemgraphCypherBaseVisitor.cpp
${opencypher_generated}/MemgraphCypherVisitor.cpp
)
# Provide a command to generate sources if missing. If this were a
# custom_target, it would always run and we don't want that.
add_custom_command(OUTPUT ${antlr_opencypher_generated_src}
COMMAND
${CMAKE_COMMAND} -E make_directory ${opencypher_generated}
COMMAND
java -jar ${CMAKE_SOURCE_DIR}/libs/antlr-4.6-complete.jar -Dlanguage=Cpp -visitor -o ${opencypher_generated} -package antlropencypher ${opencypher_lexer_grammar} ${opencypher_parser_grammar}
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
DEPENDS ${opencypher_lexer_grammar} ${opencypher_parser_grammar}
${opencypher_frontend}/grammar/CypherLexer.g4
${opencypher_frontend}/grammar/Cypher.g4)
# add custom target for generation
add_custom_target(generate_opencypher_parser
DEPENDS ${antlr_opencypher_generated_src})
add_library(antlr_opencypher_parser_lib STATIC ${antlr_opencypher_generated_src})
target_link_libraries(antlr_opencypher_parser_lib antlr4)
# -----------------------------------------------------------------------------
# Optional subproject configuration -------------------------------------------
option(POC "Build proof of concept binaries" OFF)
option(EXPERIMENTAL "Build experimental binaries" OFF)
option(CUSTOMERS "Build customer binaries" OFF)
option(TEST_COVERAGE "Generate coverage reports from running memgraph" OFF)
option(TOOLS "Build tools binaries" ON)
option(QUERY_MODULES "Build query modules containing custom procedures" ON)
option(MG_COMMUNITY "Build Memgraph Community Edition" OFF)
option(ASAN "Build with Address Sanitizer. To get a reasonable performance option should be used only in Release or RelWithDebInfo build " OFF)
option(TSAN "Build with Thread Sanitizer. To get a reasonable performance option should be used only in Release or RelWithDebInfo build " OFF)
option(UBSAN "Build with Undefined Behaviour Sanitizer" OFF)
option(THIN_LTO "Build with link time optimization" OFF)
if (TEST_COVERAGE)
string(TOLOWER ${CMAKE_BUILD_TYPE} lower_build_type)
@@ -214,8 +234,8 @@ if (TEST_COVERAGE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-instr-generate -fcoverage-mapping")
endif()
if (MG_COMMUNITY)
add_definitions(-DMG_COMMUNITY)
if (MG_ENTERPRISE)
add_definitions(-DMG_ENTERPRISE)
endif()
if (ASAN)
@@ -269,26 +289,12 @@ if (UBSAN)
# Make sure llvm-symbolizer binary is in path
endif()
if (THIN_LTO)
set(CMAKE_CXX_FLAGS"${CMAKE_CXX_FLAGS} -flto=thin")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -flto=thin")
endif()
# Add subprojects
include_directories(src)
add_subdirectory(src)
if(POC)
add_subdirectory(poc)
endif()
if(EXPERIMENTAL)
add_subdirectory(experimental)
endif()
if(CUSTOMERS)
add_subdirectory(customers)
endif()
# Release configuration
add_subdirectory(release)
enable_testing()
add_subdirectory(tests)
@@ -300,59 +306,3 @@ endif()
if(QUERY_MODULES)
add_subdirectory(query_modules)
endif()
# -----------------------------------------------------------------------------
# ---- Setup CPack --------
# General setup
set(CPACK_PACKAGE_NAME memgraph)
set(CPACK_PACKAGE_VENDOR "Memgraph Ltd.")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY
"High performance, in-memory, transactional graph database")
set(CPACK_PACKAGE_VERSION_MAJOR ${memgraph_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${memgraph_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${memgraph_VERSION_PATCH})
set(CPACK_PACKAGE_VERSION_TWEAK ${memgraph_VERSION_TWEAK})
set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${memgraph_VERSION}-${COMMIT_HASH}${CPACK_SYSTEM_NAME})
# DEB specific
# Instead of using "name <email>" format, we use "email (name)" to prevent
# errors due to full stop, '.' at the end of "Ltd". (See: RFC 822)
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "tech@memgraph.com (Memgraph Ltd.)")
set(CPACK_DEBIAN_PACKAGE_SECTION non-free/database)
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE https://memgraph.com)
set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA
"${CMAKE_SOURCE_DIR}/release/debian/conffiles;"
"${CMAKE_SOURCE_DIR}/release/debian/copyright;"
"${CMAKE_SOURCE_DIR}/release/debian/prerm;"
"${CMAKE_SOURCE_DIR}/release/debian/postrm;"
"${CMAKE_SOURCE_DIR}/release/debian/postinst;")
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
# Description formatting is important, summary must be followed with a newline and 1 space.
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}
Contains Memgraph, the graph database. It aims to deliver developers the
speed, simplicity and scale required to build the next generation of
applications driver by real-time connected data.")
# Add `openssl` package to dependencies list. Used to generate SSL certificates.
set(CPACK_DEBIAN_PACKAGE_DEPENDS "openssl (>= 1.1.0)")
# RPM specific
set(CPACK_RPM_PACKAGE_URL https://memgraph.com)
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION
/var /var/lib /var/log /etc/logrotate.d
/lib /lib/systemd /lib/systemd/system /lib/systemd/system/memgraph.service)
set(CPACK_RPM_PACKAGE_REQUIRES_PRE "shadow-utils")
# NOTE: user specfile has a bug in cmake 3.7.2, this needs to be patched
# manually in: ~/cmake/share/cmake-3.7/Modules/CPackRPM.cmake line 2273
# Or newer cmake version used
set(CPACK_RPM_USER_BINARY_SPECFILE "${CMAKE_SOURCE_DIR}/release/rpm/memgraph.spec.in")
# Description formatting is important, no line must be greater than 80 characters.
set(CPACK_RPM_PACKAGE_DESCRIPTION "Contains Memgraph, the graph database.
It aims to deliver developers the speed, simplicity and scale required to build
the next generation of applications driver by real-time connected data.")
# Add `openssl` package to dependencies list. Used to generate SSL certificates.
set(CPACK_RPM_PACKAGE_REQUIRES "openssl >= 1.0.0, curl >= 7.29.0")
# All variables must be set before including.
include(CPack)
# ---- End Setup CPack ----

4
CODEOWNERS Normal file
View File

@@ -0,0 +1,4 @@
/docs/ @gitbuda
/src/communication/ @antonio2368
/src/query/ @the-joksim
/src/storage/ @antonio2368

View File

@@ -51,7 +51,7 @@ PROJECT_BRIEF = "The World's Most Powerful Graph Database"
# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
# the logo to the output directory.
PROJECT_LOGO = Doxylogo.png
PROJECT_LOGO = docs/doxygen/memgraph_logo.png
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is
@@ -839,7 +839,6 @@ EXCLUDE_PATTERNS += */Testing/*
EXCLUDE_PATTERNS += */tests/*
EXCLUDE_PATTERNS += */dist/*
EXCLUDE_PATTERNS += */tools/*
EXCLUDE_PATTERNS += */customers/*
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the

View File

@@ -1,24 +1,47 @@
# memgraph
# Memgraph
Memgraph is an ACID compliant high performance transactional distributed
in-memory graph database featuring runtime native query compiling, lock free
data structures, multi-version concurrency control and asynchronous IO.
## dependencies
## Development Documentation
Memgraph can be compiled using any modern c++ compiler. It mostly relies on
the standard template library, however, some things do require external
libraries.
* [Quick Start](docs/dev/quick-start.md)
* [Workflow](docs/dev/workflow.md)
* [Storage](docs/dev/storage/v2/contents.md)
* [Query Engine](docs/dev/query/contents.md)
* [Communication](docs/dev/communication/contents.md)
* [Lisp C++ Preprocessor (LCP)](docs/dev/lcp.md)
Some code contains linux-specific libraries and the build is only supported
on a 64 bit linux kernel.
## Feature Specifications
* linux
* clang 3.8 (good c++11 support, especially lock free atomics)
* antlr (compiler frontend)
* cppitertools
* fmt format
* google benchmark
* google test
* glog
* gflags
Each prominent Memgraph feature requires a feature specification. The purpose
of the feature specification is to have a base for discussing all aspects of
the feature. Elements of feature specifications should be:
* High-level context.
* Interface.
* User stories. Usage from the end-user perspective. In the case of a library,
that should be cases on how to use the programming interface. In the case of
a shell script, that should be cases on how to use flags.
* Discussion about concurrency, memory management, error management.
* Any other essential functional or non-functional requirements.
* Test and benchmark strategy.
* Possible future changes/improvements/extensions.
* Security concerns.
* Additional and/or optional implementation details.
It's crucial to keep feature spec up-to-date with the implementation. Take a
look at the list of [feature specifications](docs/feature_spec/contents.md) to
learn more about powerful Memgraph features.
## User Documentation
Memgraph user documentation is maintained within
[docs](https://github.com/memgraph/docs) repository. The documentation is also
available on [GitBook](https://docs.memgraph.com).
## Licences
* [Community](release/LICENSE_COMMUNITY.md)
* [Enterprise](release/LICENSE_ENTERPRISE.md)

View File

@@ -1,39 +0,0 @@
#!/usr/bin/env python3
import json
import os
import re
import subprocess
import sys
# paths
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
BUILD_OUTPUT_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, "build_release", "output"))
# helpers
def run_cmd(cmd, cwd):
return subprocess.run(cmd, cwd=cwd, check=True,
stdout=subprocess.PIPE).stdout.decode("utf-8")
# check project
if re.search(r"release", os.environ.get("PROJECT", "")) is None:
print(json.dumps([]))
sys.exit(0)
# generate archive
deb_name = run_cmd(["find", ".", "-maxdepth", "1", "-type", "f",
"-name", "memgraph*.deb"], BUILD_OUTPUT_DIR).split("\n")[0][2:]
arch = run_cmd(["dpkg", "--print-architecture"], BUILD_OUTPUT_DIR).split("\n")[0]
version = deb_name.split("-")[1]
# generate Debian package file name as expected by Debian Policy
standard_deb_name = "memgraph_{}-1_{}.deb".format(version, arch)
archive_path = os.path.relpath(os.path.join(BUILD_OUTPUT_DIR,
deb_name), SCRIPT_DIR)
archives = [{
"name": "Release (deb package)",
"archive": archive_path,
"filename": standard_deb_name,
}]
print(json.dumps(archives, indent=4, sort_keys=True))

View File

@@ -1,15 +0,0 @@
- name: Binaries
archive:
- build_debug/memgraph
- build_release/memgraph
- build_release/tools/src/mg_client
- build_release/tools/src/mg_import_csv
- config
filename: binaries.tar.gz
- name: Doxygen documentation
cd: docs/doxygen/html
archive:
- .
filename: documentation.tar.gz
host: true

View File

@@ -1,94 +0,0 @@
- name: Diff build
project: ^mg-master-diff$
commands: |
# Activate toolchain
export PATH=/opt/toolchain-v1/bin:$PATH
export LD_LIBRARY_PATH=/opt/toolchain-v1/lib:/opt/toolchain-v1/lib64
# Copy untouched repository to parent folder.
cd ..
cp -r memgraph parent
cd memgraph
# Initialize and create documentation.
TIMEOUT=1200 ./init
doxygen Doxyfile
# Remove default build directory.
rm -r build
# Build debug binaries.
mkdir build_debug
cd build_debug
cmake ..
TIMEOUT=1200 make -j$THREADS
# Build coverage binaries.
cd ..
# TODO: uncomment this build once single node and ha are split
# mkdir build_coverage
# cd build_coverage
# cmake -DTEST_COVERAGE=ON ..
# TIMEOUT=1200 make -j$THREADS memgraph__unit
ln -s build_debug build_coverage
# Build release binaries.
# cd ..
mkdir build_release
cd build_release
cmake -DCMAKE_BUILD_TYPE=release ..
TIMEOUT=1200 make -j$THREADS
cd ..
# Checkout to parent commit and initialize.
cd ../parent
git checkout HEAD~1
TIMEOUT=1200 ./init
# Build parent release binaries.
mkdir build_release
cd build_release
cmake -DCMAKE_BUILD_TYPE=release ..
TIMEOUT=1200 make -j$THREADS memgraph memgraph__macro_benchmark
# release build is the default one
- name: Release build
commands: |
# Activate toolchain
export PATH=/opt/toolchain-v1/bin:$PATH
export LD_LIBRARY_PATH=/opt/toolchain-v1/lib:/opt/toolchain-v1/lib64
# Initialize and create documentation.
TIMEOUT=1200 ./init
doxygen Doxyfile
# Remove default build directory.
rm -r build
# Build debug binaries.
mkdir build_debug
cd build_debug
cmake ..
TIMEOUT=1200 make -j$THREADS
# Build coverage binaries.
cd ..
# TODO: uncomment this build once single node and ha are split
# mkdir build_coverage
# cd build_coverage
# cmake -DTEST_COVERAGE=ON ..
# TIMEOUT=1200 make -j$THREADS memgraph__unit
ln -s build_debug build_coverage
# Build release binaries.
# cd ..
mkdir build_release
cd build_release
cmake -DCMAKE_BUILD_TYPE=Release -DUSE_READLINE=OFF ..
TIMEOUT=1200 make -j$THREADS
# Create Debian package.
mkdir output
cd output
cpack -G DEB --config ../CPackConfig.cmake

View File

@@ -39,14 +39,6 @@ modifications:
value: "/var/log/memgraph/memgraph.log"
override: true
- name: "bolt_cert_file"
value: "/etc/memgraph/ssl/cert.pem"
override: true
- name: "bolt_key_file"
value: "/etc/memgraph/ssl/key.pem"
override: true
- name: "bolt_num_workers"
value: ""
override: false

View File

@@ -1,2 +0,0 @@
project(mg_customers)
add_subdirectory(otto)

View File

@@ -1,70 +0,0 @@
DISCLAIMER: this is just an initial test, graph might not resemble
the graph in the use case at all and the data might be completely
irrelevant.
We tried generating a few sample graphs from the vague description
given in the use case doc. Then we tried writing queries that would
solve the problem of updating nodes when a leaf value changes,
assuming all the internal nodes compute only the sum function.
We start by creating an index on `id` property to improve initial lookup
performance:
CREATE INDEX ON :Leaf(id)
Set values of all leafs to 1:
MATCH (u:Leaf) SET u.value = 1
Now we initialize the values of all other nodes in the graph:
MATCH (u) WHERE NOT u:Leaf SET u.value = 0
MATCH (u) WITH u
ORDER BY u.topological_index DESC
MATCH (u)-->(v) SET u.value = u.value + v.value
Change the value of a leaf:
MATCH (u:Leaf {id: "18"}) SET u.value = 10
We have to reset all the updated nodes to a neutral element:
MATCH (u:Leaf {id: "18"})<-[* bfs]-(v)
WHERE NOT v:Leaf SET v.value = 0
Finally, we recalculate their values in topological order:
MATCH (u:Leaf {id: "18"})<-[* bfs]-(v)
WITH v ORDER BY v.topological_index DESC
MATCH (v)-->(w) SET v.value = v.value + w.value
There are a few assumptions made worth pointing out.
* We are able to efficiently maintain topological order
of vertices in the graph.
* It is possible to accumulate the value of the function. Formally:
$$f(x_1, x_2, ..., x_n) = g(...(g(g(x_1, x_2), x_3), ...), x_n)$$
* There is a neutral element for the operation. However, this
assumption can be dropped by introducing an artificial neutral element.
Number of operations required is proportional to the sum of degrees of affected
nodes.
We generated graph with $10^5$ nodes ($20\ 000$ nodes in each layer), varied the
degree distribution in node layers and measured time for the query to execute:
| # | Root-Category-Group degree | Group-CustomGroup-Leaf degree | Time |
|:-:|:---------------------------:|:-----------------------------:|:---------:|
| 1 | [1, 10] | [20, 40] | ~1.1s |
| 2 | [1, 10] | [50, 100] | ~2.5s |
| 3 | [10, 50] | [50, 100] | ~3.3s |
Due to the structure of the graph, update of a leaf required update of almost
all the nodes in the graph so we don't show times required for initial graph
update and update after leaf change separately.
However, there is not enough info on the use case to make the test more
sophisticated.

View File

@@ -1,71 +0,0 @@
---
title: "Elliott Management"
subtitle: "Proof of Concept Report"
header-title: "Elliott Management POC"
date: 2017-10-28
copyright: "©2017 Memgraph Ltd. All rights reserved."
titlepage: true
titlepage-color: FFFFFF
titlepage-text-color: 101010
titlepage-rule-color: 101010
titlepage-rule-height: 1
...
# Introduction
We tried generating a few sample graphs from the description given at
the in-person meetings. Then, we tried writing queries that would solve
the problem of updating nodes when a leaf value changes, assuming all the
internal nodes compute only the sum function.
# Technical details
We started by creating an index on `id` property to improve initial lookup
performance:
CREATE INDEX ON :Leaf(id)
Afther that, we set values of all leafs to 1:
MATCH (u:Leaf) SET u.value = 1
We then initialized the values of all other nodes in the graph:
MATCH (u) WHERE NOT u:Leaf SET u.value = 0
MATCH (u) WITH u
ORDER BY u.topological_index DESC
MATCH (u)-->(v) SET u.value = u.value + v.value
Leaf value change and update of affected values in the graph can
be done using three queries. To change the value of a leaf:
MATCH (u:Leaf {id: "18"}) SET u.value = 10
Then we had to reset all the affected nodes to the neutral element:
MATCH (u:Leaf {id: "18"})<-[* bfs]-(v)
WHERE NOT v:Leaf SET v.value = 0
Finally, we recalculated their values in topological order:
MATCH (u:Leaf {id: "18"})<-[* bfs]-(v)
WITH v ORDER BY v.topological_index DESC
MATCH (v)-->(w) SET v.value = v.value + w.value
There are a few assumptions necessary for the approach above to work.
* We are able to maintain topological order of vertices during graph
structure changes.
* It is possible to accumulate the value of the function. Formally:
$$f(x_1, x_2, ..., x_n) = g(...(g(g(x_1, x_2), x_3), ...), x_n)$$
* There is a neutral element for the operation. However, this
assumption can be dropped by introducing an artificial neutral element.
Above assumptions could be changed, relaxed or dropped, depending on the
specifics of the use case.
Number of operations required is proportional to the sum of degrees of affected
nodes.

View File

@@ -1,12 +0,0 @@
CREATE INDEX ON :Leaf(id);
MATCH (u:Leaf) SET u.value = 1;
MATCH (u) WHERE NOT u:Leaf SET u.value = 0;
MATCH (u) WITH u
ORDER BY u.topological_index DESC
MATCH (u)-->(v) SET u.value = u.value + v.value;
MATCH (u:Leaf {id: "85000"}) SET u.value = 10;
MATCH (u:Leaf {id: "85000"})<-[* bfs]-(v)
WHERE NOT v:Leaf SET v.value = 0;
MATCH (u:Leaf {id: "85000"})<-[* bfs]-(v)
WITH v ORDER BY v.topological_index DESC
MATCH (v)-->(w) SET v.value = v.value + w.value;

View File

@@ -1,125 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Generates a DAG from JSON spec in [config] and outputs nodes to
[filename]_nodes, and edges to [filename]_edges in format convertible
to Memgraph snapshot.
Here's an example JSON spec:
{
"layers": [
{
"name": "A",
"sublayers": 1,
"degree_lo": 1,
"degree_hi": 3,
"nodes": 4
},
{
"name": "B",
"sublayers": 3,
"degree_lo": 2,
"degree_hi": 3,
"nodes": 10
},
{
"name": "C",
"sublayers": 1,
"degree_lo": 1,
"degree_hi": 1,
"nodes": 5
}
]
}
Nodes from each layer will be randomly divided into sublayers. A node can
only have edges pointing to nodes in lower sublayers of the same layer, or
to nodes from the layer directly below it. Out-degree is chosen uniformly
random from [degree_lo, degree_hi] interval."""
import argparse
from itertools import accumulate
import json
import random
def _split_into_sum(n, k):
assert 1 <= n, "n should be at least 1"
assert k <= n, "k shouldn't be greater than n"
xs = [0] + sorted(random.sample(range(1, n), k-1)) + [n]
return [b - a for a, b in zip(xs, xs[1:])]
def generate_dag(graph_config, seed=None):
random.seed(seed)
nodes = []
edges = []
layer_lo = 1
for layer in graph_config:
sublayers = _split_into_sum(layer['nodes'], layer['sublayers'])
sub_range = accumulate([layer_lo] + sublayers)
layer['sublayer_range'] = list(sub_range)
nodes.extend([
(u, layer['name'])
for u in range(layer_lo, layer_lo + layer['nodes'])
])
layer_lo += layer['nodes']
edges = []
for layer, next_layer in zip(graph_config, graph_config[1:]):
degree_lo = layer['degree_lo']
degree_hi = layer['degree_hi']
sub_range = layer['sublayer_range']
sub_range_next = next_layer['sublayer_range']
layer_lo = sub_range[0]
next_layer_hi = sub_range_next[-1]
for sub_lo, sub_hi in zip(sub_range, sub_range[1:]):
for u in range(sub_lo, sub_hi):
num_edges = random.randint(degree_lo, degree_hi)
for _ in range(num_edges):
v = random.randint(sub_hi, next_layer_hi - 1)
edges.append((u, v))
for sub_lo, sub_hi in zip(sub_range_next, sub_range_next[1:]):
for u in range(sub_lo, sub_hi):
v = random.randint(layer_lo, sub_lo - 1)
edges.append((v, u))
return nodes, edges
if __name__ == '__main__':
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description=__doc__)
parser.add_argument('config', type=str, help='graph config JSON file')
parser.add_argument('filename', type=str,
help='nodes will be stored to filename_nodes, '
'edges to filename_edges')
parser.add_argument('--seed', type=int,
help='seed for the random generator (default = '
'current system time)')
args = parser.parse_args()
with open(args.config, 'r') as f:
graph_config = json.loads(f.read())['layers']
nodes, edges = generate_dag(graph_config, seed=args.seed)
# print nodes into CSV file
with open('{}_nodes'.format(args.filename), 'w') as out:
out.write('nodeId:ID(Node),name,topological_index:Int,:LABEL\n')
for node_id, layer in nodes:
out.write('{0},{1}{0},{0},{1}\n'.format(node_id, layer))
# print edges into CSV file
with open('{}_edges'.format(args.filename), 'w') as out:
out.write(':START_ID(Node),:END_ID(Node),:TYPE\n')
for u, v in edges:
out.write('{},{},child\n'.format(u, v))

View File

@@ -1,39 +0,0 @@
{
"layers": [
{
"name": "Root",
"sublayers": 1,
"degree_lo": 1,
"degree_hi": 10,
"nodes": 20000
},
{
"name": "Category",
"sublayers": 5,
"degree_lo": 1,
"degree_hi": 10,
"nodes": 20000
},
{
"name": "Group",
"sublayers": 1,
"degree_lo": 20,
"degree_hi": 40,
"nodes": 20000
},
{
"name": "CustomGroup",
"sublayers": 15,
"degree_lo": 20,
"degree_hi": 40,
"nodes": 20000
},
{
"name": "Leaf",
"sublayers": 1,
"degree_lo": 1,
"degree_hi": 1,
"nodes": 20000
}
]
}

View File

@@ -1,39 +0,0 @@
{
"layers": [
{
"name": "Root",
"sublayers": 1,
"degree_lo": 1,
"degree_hi": 10,
"nodes": 20000
},
{
"name": "Category",
"sublayers": 5,
"degree_lo": 1,
"degree_hi": 10,
"nodes": 20000
},
{
"name": "Group",
"sublayers": 1,
"degree_lo": 50,
"degree_hi": 100,
"nodes": 20000
},
{
"name": "CustomGroup",
"sublayers": 15,
"degree_lo": 50,
"degree_hi": 100,
"nodes": 20000
},
{
"name": "Leaf",
"sublayers": 1,
"degree_lo": 50,
"degree_hi": 100,
"nodes": 20000
}
]
}

View File

@@ -1,39 +0,0 @@
{
"layers": [
{
"name": "Root",
"sublayers": 1,
"degree_lo": 10,
"degree_hi": 50,
"nodes": 20000
},
{
"name": "Category",
"sublayers": 5,
"degree_lo": 10,
"degree_hi": 50,
"nodes": 20000
},
{
"name": "Group",
"sublayers": 1,
"degree_lo": 50,
"degree_hi": 100,
"nodes": 20000
},
{
"name": "CustomGroup",
"sublayers": 15,
"degree_lo": 50,
"degree_hi": 100,
"nodes": 20000
},
{
"name": "Leaf",
"sublayers": 1,
"degree_lo": 50,
"degree_hi": 100,
"nodes": 20000
}
]
}

View File

@@ -1,3 +0,0 @@
set(exec_name customers_otto_parallel_connected_components)
add_executable(${exec_name} parallel_connected_components.cpp)
target_link_libraries(${exec_name} memgraph_lib)

View File

@@ -1,118 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
This script attempts to evaluate the feasibility of using Memgraph for
Otto group's usecase. The usecase is finding connected componentes in
a large, very sparse graph (cca 220M nodes, 250M edges), based on a dynamic
inclusion / exclusion of edges (w.r.t variable parameters and the source node
type).
This implementation defines a random graph with the given number of nodes
and edges and looks for connected components using breadth-first expansion.
Edges are included / excluded based on a simple expression, only demonstrating
possible usage.
"""
from argparse import ArgumentParser
import logging
from time import time
from collections import defaultdict
from math import log2
from random import randint
from neo4j.v1 import GraphDatabase
log = logging.getLogger(__name__)
def generate_graph(sess, node_count, edge_count):
# An index that will speed-up edge creation.
sess.run("CREATE INDEX ON :Node(id)").consume()
# Create the given number of nodes with a randomly selected type from:
# [0.5, 1.5, 2.5].
sess.run(("UNWIND range(0, {} - 1) AS id CREATE "
"(:Node {{id: id, type: 0.5 + tointeger(rand() * 3)}})").format(
node_count)).consume()
# Create the given number of edges, each with a 'value' property of
# a random [0, 3.0) float. Each edge connects two random nodes, so the
# expected node degree is (edge_count * 2 / node_count). Generate edges
# so the connectivity is non-uniform (to produce connected components of
# various sizes).
sess.run(("UNWIND range(0, {0} - 1) AS id WITH id "
"MATCH (from:Node {{id: tointeger(rand() * {1})}}), "
"(to:Node {{id: tointeger(rand() * {1} * id / {0})}}) "
"CREATE (from)-[:Edge {{value: 3 * rand()}}]->(to)").format(
edge_count, node_count)).consume()
def get_connected_ids(sess, node_id):
# Matches a node with the given ID and returns the IDs of all the nodes
# it is connected to. Note that within the BFS lambda expression there
# is an expression used to filter out edges expanded over.
return sess.run((
"MATCH (from:Node {{id: {}}})-"
"[*bfs (e, n | abs(from.type - e.value) < 0.80)]-(d) "
"RETURN count(*) AS c").format(node_id)).data()[0]['c']
def parse_args():
parser = ArgumentParser(description=__doc__)
parser.add_argument('--endpoint', type=str, default='localhost:7687',
help='Memgraph instance endpoint. ')
parser.add_argument('--node-count', type=int, default=1000,
help='The number of nodes in the graph')
parser.add_argument('--edge-count', type=int, default=1000,
help='The number of edges in the graph')
parser.add_argument('--sample-count', type=int, default=None,
help='The number of samples to take')
return parser.parse_args()
def main():
args = parse_args()
logging.basicConfig(level=logging.INFO)
log.info("Memgraph - Otto test database generator")
logging.getLogger("neo4j").setLevel(logging.WARNING)
driver = GraphDatabase.driver(
'bolt://' + args.endpoint,
auth=("ignored", "ignored"),
encrypted=False)
sess = driver.session()
sess.run("MATCH (n) DETACH DELETE n").consume()
log.info("Generating graph with %s nodes and %s edges...",
args.node_count, args.edge_count)
generate_graph(sess, args.node_count, args.edge_count)
# Track which vertices have been found as part of a component.
start_time = time()
max_query_time = 0
log.info("Looking for connected components...")
# Histogram of log2 sizes of connected components found.
histogram = defaultdict(int)
sample_count = args.sample_count if args.sample_count else args.node_count
for i in range(sample_count):
node_id = randint(0, args.node_count - 1)
query_start_time = time()
log2_size = int(log2(1 + get_connected_ids(sess, node_id)))
max_query_time = max(max_query_time, time() - query_start_time)
histogram[log2_size] += 1
elapsed = time() - start_time
log.info("Connected components found in %.2f sec (avg %.2fms, max %.2fms)",
elapsed, elapsed / sample_count * 1000, max_query_time * 1000)
log.info("Component size histogram (count | range)")
for log2_size, count in sorted(histogram.items()):
log.info("\t%5d | %d - %d", count, 2 ** log2_size,
2 ** (log2_size + 1) - 1)
sess.close()
driver.close()
if __name__ == '__main__':
main()

View File

@@ -1,207 +0,0 @@
#include <algorithm>
#include <limits>
#include <mutex>
#include <random>
#include <set>
#include <stack>
#include <thread>
#include "gflags/gflags.h"
#include "glog/logging.h"
#include "data_structures/union_find.hpp"
#include "database/graph_db.hpp"
#include "database/graph_db_accessor.hpp"
#include "storage/property_value.hpp"
#include "threading/sync/spinlock.hpp"
#include "utils/bound.hpp"
#include "utils/timer.hpp"
DEFINE_int32(thread_count, 1, "Number of threads");
DEFINE_int32(vertex_count, 1000, "Number of vertices");
DEFINE_int32(edge_count, 1000, "Number of edges");
DECLARE_int32(gc_cycle_sec);
static const std::string kLabel{"kLabel"};
static const std::string kProperty{"kProperty"};
void GenerateGraph(database::GraphDb &db) {
{
database::GraphDbAccessor dba{db};
dba.BuildIndex(dba.Label(kLabel), dba.Property(kProperty));
dba.Commit();
}
// Randomize the sequence of IDs of created vertices and edges to simulate
// real-world lack of locality.
auto make_id_vector = [](size_t size) {
gid::Generator generator{0};
std::vector<gid::Gid> ids(size);
for (size_t i = 0; i < size; ++i)
ids[i] = generator.Next(std::experimental::nullopt);
std::random_shuffle(ids.begin(), ids.end());
return ids;
};
std::vector<VertexAccessor> vertices;
vertices.reserve(FLAGS_vertex_count);
{
CHECK(FLAGS_vertex_count % FLAGS_thread_count == 0)
<< "Thread count must be a factor of vertex count";
LOG(INFO) << "Generating " << FLAGS_vertex_count << " vertices...";
utils::Timer timer;
auto vertex_ids = make_id_vector(FLAGS_vertex_count);
std::vector<std::thread> threads;
SpinLock vertices_lock;
for (int i = 0; i < FLAGS_thread_count; ++i) {
threads.emplace_back([&db, &vertex_ids, &vertices, &vertices_lock, i]() {
database::GraphDbAccessor dba{db};
auto label = dba.Label(kLabel);
auto property = dba.Property(kProperty);
auto batch_size = FLAGS_vertex_count / FLAGS_thread_count;
for (int j = i * batch_size; j < (i + 1) * batch_size; ++j) {
auto vertex = dba.InsertVertex(vertex_ids[j]);
vertex.add_label(label);
vertex.PropsSet(property, static_cast<int64_t>(vertex_ids[j]));
vertices_lock.lock();
vertices.emplace_back(vertex);
vertices_lock.unlock();
}
dba.Commit();
});
}
for (auto &t : threads) t.join();
LOG(INFO) << "Generated " << FLAGS_vertex_count << " vertices in "
<< timer.Elapsed().count() << " seconds.";
}
{
database::GraphDbAccessor dba{db};
for (int i = 0; i < FLAGS_vertex_count; ++i)
vertices[i] = *dba.Transfer(vertices[i]);
LOG(INFO) << "Generating " << FLAGS_edge_count << " edges...";
auto edge_ids = make_id_vector(FLAGS_edge_count);
std::mt19937 pseudo_rand_gen{std::random_device{}()};
std::uniform_int_distribution<> rand_dist{0, FLAGS_vertex_count - 1};
auto edge_type = dba.EdgeType("edge");
utils::Timer timer;
for (int i = 0; i < FLAGS_edge_count; ++i)
dba.InsertEdge(vertices[rand_dist(pseudo_rand_gen)],
vertices[rand_dist(pseudo_rand_gen)], edge_type,
edge_ids[i]);
dba.Commit();
LOG(INFO) << "Generated " << FLAGS_edge_count << " edges in "
<< timer.Elapsed().count() << " seconds.";
}
}
auto EdgeIteration(database::GraphDb &db) {
database::GraphDbAccessor dba{db};
int64_t sum{0};
for (auto edge : dba.Edges(false)) sum += edge.from().gid() + edge.to().gid();
return sum;
}
auto VertexIteration(database::GraphDb &db) {
database::GraphDbAccessor dba{db};
int64_t sum{0};
for (auto v : dba.Vertices(false))
for (auto e : v.out()) sum += e.gid() + e.to().gid();
return sum;
}
auto ConnectedComponentsEdges(database::GraphDb &db) {
UnionFind<int64_t> connectivity{FLAGS_vertex_count};
database::GraphDbAccessor dba{db};
for (auto edge : dba.Edges(false))
connectivity.Connect(edge.from().gid(), edge.to().gid());
return connectivity.Size();
}
auto ConnectedComponentsVertices(database::GraphDb &db) {
UnionFind<int64_t> connectivity{FLAGS_vertex_count};
database::GraphDbAccessor dba{db};
for (auto from : dba.Vertices(false)) {
for (auto out_edge : from.out())
connectivity.Connect(from.gid(), out_edge.to().gid());
}
return connectivity.Size();
}
auto ConnectedComponentsVerticesParallel(database::GraphDb &db) {
UnionFind<int64_t> connectivity{FLAGS_vertex_count};
SpinLock connectivity_lock;
// Define bounds of vertex IDs for each thread to use.
std::vector<PropertyValue> bounds;
for (int64_t i = 0; i < FLAGS_thread_count; ++i)
bounds.emplace_back(i * FLAGS_vertex_count / FLAGS_thread_count);
bounds.emplace_back(std::numeric_limits<int64_t>::max());
std::vector<std::thread> threads;
for (int i = 0; i < FLAGS_thread_count; ++i) {
threads.emplace_back(
[&connectivity, &connectivity_lock, &bounds, &db, i]() {
database::GraphDbAccessor dba{db};
for (auto from :
dba.Vertices(dba.Label(kLabel), dba.Property(kProperty),
utils::MakeBoundInclusive(bounds[i]),
utils::MakeBoundExclusive(bounds[i + 1]), false)) {
for (auto out_edge : from.out()) {
std::lock_guard<SpinLock> lock{connectivity_lock};
connectivity.Connect(from.gid(), out_edge.to().gid());
}
}
});
}
for (auto &t : threads) t.join();
return connectivity.Size();
}
auto Expansion(database::GraphDb &db) {
std::vector<int> component_ids(FLAGS_vertex_count, -1);
int next_component_id{0};
std::stack<VertexAccessor> expansion_stack;
database::GraphDbAccessor dba{db};
for (auto v : dba.Vertices(false)) {
if (component_ids[v.gid()] != -1) continue;
auto component_id = next_component_id++;
expansion_stack.push(v);
while (!expansion_stack.empty()) {
auto next_v = expansion_stack.top();
expansion_stack.pop();
if (component_ids[next_v.gid()] != -1) continue;
component_ids[next_v.gid()] = component_id;
for (auto e : next_v.out()) expansion_stack.push(e.to());
for (auto e : next_v.in()) expansion_stack.push(e.from());
}
}
return next_component_id;
}
int main(int argc, char **argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
FLAGS_gc_cycle_sec = -1;
database::SingleNode db;
GenerateGraph(db);
auto timed_call = [&db](auto callable, const std::string &descr) {
LOG(INFO) << "Running " << descr << "...";
utils::Timer timer;
auto result = callable(db);
LOG(INFO) << "\tDone in " << timer.Elapsed().count()
<< " seconds, result: " << result;
};
timed_call(EdgeIteration, "Edge iteration");
timed_call(VertexIteration, "Vertex iteration");
timed_call(ConnectedComponentsEdges, "Connected components - Edges");
timed_call(ConnectedComponentsVertices, "Connected components - Vertices");
timed_call(ConnectedComponentsVerticesParallel,
"Parallel connected components - Vertices");
timed_call(Expansion, "Expansion");
return 0;
}

View File

@@ -1,5 +0,0 @@
WITH tointeger(rand() * 40000000) AS from_id
MATCH (from:Node {id : from_id}) WITH from
MATCH path = (from)-[*bfs..50 (e, n | degree(n) < 50)]->(to) WITH path LIMIT 10000 WHERE to.fraudulent
RETURN path, size(path)

View File

@@ -1,31 +0,0 @@
{
"indexes":[
"Node.id"
],
"nodes":[
{
"count":40000000,
"labels":[
"Node"
],
"properties":{
"id":{
"type":"counter",
"param":"Node.id"
},
"fraudulent":{
"type":"bernoulli",
"param":0.0005
}
}
}
],
"edges":[
{
"count":80000000,
"from":"Node",
"to":"Node",
"type":"Edge"
}
]
}

View File

@@ -1,20 +0,0 @@
{
"indexes" : ["Card.id", "Pos.id", "Transaction.fraud_reported"],
"nodes" : [
{
"count_per_worker" : 1250000,
"label" : "Card"
},
{
"count_per_worker" : 1250000,
"label" : "Pos"
},
{
"count_per_worker" : 2500000,
"label" : "Transaction"
}
],
"compromised_pos_probability" : 0.2,
"fraud_reported_probability" : 0.1,
"hop_probability" : 0.1
}

View File

@@ -0,0 +1,5 @@
# Communication
## Bolt
Memgraph implements [Bolt communication protocol](https://7687.org/).

View File

@@ -29,6 +29,33 @@ You should use a pointer to `const` then. The primary reason being is that
references obscure the semantics of moving an object, thus making bugs with
references pointing to invalid memory harder to track down.
Example of this can be seen while capturing member variables by reference
inside a lambda.
Let's define a class that has two members, where one of those members is a
lambda that captures the other member by reference.
```cpp
struct S {
std::function<void()> foo;
int bar;
S() : foo([&]() { std::cout << bar; })
{}
};
```
What would happend if we move an instance of this object? Our lambda
reference capture will point to the same location as before, i.e. it
will point to the **old** memory location of `bar`. This means we have
a dangling reference in our code!
There are multiple ways to avoid this. The simple solutions would be
capturing by value or disabling move constructors/assignments.
Still, if we capture by reference an object that is not a member
of the struct containing the lambda, we can still have a dangling
reference if we move that object somewhere in our code and there is
nothing we can do to prevent that.
So, be careful with lambda catptures, and remember that references are
still a pointer under the hood!
[Style guide reference](https://google.github.io/styleguide/cppguide.html#Reference_Arguments)
#### Constructors & RAII
@@ -47,6 +74,66 @@ apply to destructors, which are not allowed to even throw exceptions.
[Style guide reference](https://google.github.io/styleguide/cppguide.html#Doing_Work_in_Constructors)
#### Constructors and member variables
One of the most powerful tools in C++ is the move semantics. We won't go into
detail how it works, but you should know how to utilize it as much as
possible. In our example we will define a small `struct` called `S` which
contains only a single member, `text` of type `std::string`.
```cpp
struct S {
std::string text;
};
```
We want to define a constructor that takes a `std::string`, and saves its
value in `text`. This is a common situation, where the constructor takes
a value, and saves it in the object to be constructed.
Our first implementation would look like this:
```cpp
S(const std::string &s) : text(s) {}
```
This is a valid solution but with one downside - we always copy. If we
construct an object like this:
```cpp
S s("some text");
```
we would create a temporary `std::string` object and then copy it to our member
variable.
Of course, we know what to do now - we will capture temporary variables using
`&&` and move it into our `text` variable.
```cpp
S(std::string &&s) : text(std::move(s)) {}
```
Now let's add an extra member variable of type `std::vector<int>` called
`words`. Our constructors accept 2 values now - `std::vector<int>` and
`std::string`. Those arguments could be passed by value, by reference, or
as rvalues. To cover all the cases we need to define a dedicated constructor
for each case.
Fortunately, there are two simpler options, the first one is writing a
templated constructor:
```cpp
template<typename T1, typename T2>
S(T1 &&s, T2 &&v) : text(std::forward<T1>(s), words(std::forward<T2>(v) {}
```
But don't forget to define `requires` clause so you don't accept any type. This
solution is optimal but really hard to read AND write. The second solution is
something you should ALWAYS prefer in these simple cases where we only store
one of the arguments:
```cpp
S(std::string s, std::vector<int> v) : text(std::move(s)), words(std::move(v)) {}
```
This way we have an almost optimal solution. The only extra operation we have is
the extra move when we send an `lvalue`. We would copy the value to the `s`, and
then move it to the `text` variable. Before, we would copy directly to `text`.
Also, you should ALWAYS write const-correct code, meaning `s` and `v` cannot be
`const` as it's not correct here. Why is that? You CANNOT move a const object!
It would just degrade to copying the object. I would say that this is a small
price to pay for a much cleaner and more maintainable code.
### Additional Style Conventions
Old code may have broken Google C++ Style accidentally, but the new code

View File

@@ -1,288 +0,0 @@
// dot -Tpng dependencies.dot -o /path/to/output.png
// TODO (buda): Put PropertyValueStore to storage namespace
digraph {
// At the beginning of each block there is a default style for that block
label="Memgraph Dependencies Diagram"; fontname="Roboto Bold"; fontcolor=black;
fontsize=26; labelloc=top; labeljust=right;
compound=true; // If true, allow edges between clusters
rankdir=TB; // Alternatives: LR
node [shape=record fontname="Roboto", fontsize=12, fontcolor=white];
edge [color="#B5AFB7"];
// -- Legend --
// dir=both arrowtail=diamond arrowhead=vee -> group ownership
// dir=both arrowtail=none, arrowhead=vee -> ownership; stack or uptr
subgraph cluster_tcp_end_client_communication {
label="TCP End Client Communication"; fontsize=14;
node [style=filled, color="#DD2222" fillcolor="#DD2222"];
// Owned elements
"communication::Server";
"io::network::Socket";
// Intracluster connections
"communication::Server" -> "io::network::Socket"
[label="socket_" dir=both arrowtail=none arrowhead=vee];
}
subgraph cluster_bolt_server {
label="Bolt Server"; fontsize=14;
node [style=filled, color="#62A2CA" fillcolor="#62A2CA"];
// Owned elements
"communication::bolt::SessionData";
"communication::bolt::Session";
"communication::bolt::Encoder";
"communication::bolt::Decoder";
// Intracluster connections
"communication::bolt::Session" -> "communication::bolt::Encoder"
[label="encoder_", dir=both arrowtail=none, arrowhead=vee];
"communication::bolt::Session" -> "communication::bolt::Decoder"
[label="decoder_", dir=both arrowtail=none, arrowhead=vee];
}
subgraph cluster_opencypher_engine {
label="openCypher Engine"; fontsize=14;
node [style=filled, color="#68BDF6" fillcolor="#68BDF6"];
// Owned Elements
"query::Interpreter";
"query::AstTreeStorage";
"query::TypedValue"
"query::Path";
"query::Simbol";
"query::Context";
"query::ExpressionEvaluator";
"query::Frame";
"query::SymbolTable";
"query::plan::LogicalOperator";
"query::plan::Cursor";
"query::plan::CostEstimator";
// Intracluster connections
"query::Interpreter" -> "query::AstTreeStorage"
[label="ast_cache" dir=both arrowtail=diamond arrowhead=vee];
"query::TypedValue" -> "query::Path";
"query::plan::Cursor" -> "query::Frame";
"query::plan::Cursor" -> "query::Context";
"query::plan::LogicalOperator" -> "query::Symbol";
"query::plan::LogicalOperator" -> "query::SymbolTable";
"query::plan::LogicalOperator" -> "query::plan::Cursor";
}
subgraph cluster_storage {
label="Storage" fontsize=14;
node [style=filled, color="#FB6E00" fillcolor="#FB6E00"];
// Owned Elements
"database::GraphDb";
"database::GraphDbAccessor";
"storage::Record";
"storage::Vertex";
"storage::Edge";
"storage::RecordAccessor";
"storage::VertexAccessor";
"storage::EdgeAccessor";
"storage::Common";
"storage::Label";
"storage::EdgeType";
"storage::Property";
"storage::compression";
"storage::SingleNodeConcurrentIdMapper";
"storage::Location";
"storage::StorageTypesLocation";
"PropertyValueStore";
"storage::RecordLock";
"mvcc::Version";
"mvcc::Record";
"mvcc::VersionList";
// Intracluster connections
"storage::VertexAccessor" -> "storage::RecordAccessor"
[arrowhead=onormal];
"storage::EdgeAccessor" -> "storage::RecordAccessor"
[arrowhead=onormal];
"storage::RecordAccessor" -> "database::GraphDbAccessor"
[style=dashed arrowhead=vee];
"storage::Vertex" -> "mvcc::Record"
[arrowhead=onormal];
"storage::Edge" -> "mvcc::Record"
[arrowhead=onormal];
"storage::Edge" -> "PropertyValueStore"
[arrowhead=vee];
"storage::Vertex" -> "PropertyValueStore"
[arrowhead=vee];
"storage::Edge" -> "mvcc::VersionList"
[label="from,to" arrowhead=vee style=dashed];
"storage::VertexAccessor" -> "storage::Vertex"
[arrowhead=vee];
"storage::EdgeAccessor" -> "storage::Edge"
[arrowhead=vee];
"storage::SingleNodeConcurrentIdMapper" -> "storage::StorageTypesLocation"
[arrowhead=vee];
"storage::StorageTypesLocation" -> "storage::Location"
[arrowhead=vee];
"storage::Storage" -> "storage::StorageTypesLocation"
[arrowhead=vee];
"storage::Property" -> "storage::Common"
[arrowhead=onormal];
"storage::Label" -> "storage::Common"
[arrowhead=onormal];
"storage::EdgeType" -> "storage::Common"
[arrowhead=onormal];
"storage::Property" -> "storage::Location"
[arrowhead=vee];
"PropertyValueStore" -> "storage::Property"
[arrowhead=vee];
"PropertyValueStore" -> "storage::Location"
[arrowhead=vee];
"database::GraphDbAccessor" -> "database::GraphDb"
[arrowhead=vee];
"database::GraphDbAccessor" -> "tx::TransactionId"
[arrowhead=vee];
"mvcc::VersionList" -> "storge::RecordLock"
[label="lock" arrowhead=vee];
"mvcc::VersionList" -> "mvcc::Record"
[label="head" arrowhead=vee];
"mvcc::Record" -> "mvcc::Version"
[arrowhead=onormal];
// Explicit positioning
{rank=same;
"database::GraphDbAccessor";
"storage::VertexAccessor";
"storage::EdgeAccessor";}
{rank=same;
"storage::Common";
"storage::compression";}
}
subgraph cluster_properties_on_disk {
label="Properties on Disk" fontsize=14;
node [style=filled, color="#102647" fillcolor="#102647"];
// Owned Elements
"storage::KVStore";
"rocksdb";
// Intracluster connections
"storage::KVStore" -> "rocksdb";
}
subgraph cluster_distributed {
label="Distributed" fontsize=14;
node [style=filled, color="#FFC500" fillcolor="#FFC500"];
// Owned Elements
"distributed::DataManager";
"distributed::DataRpcClients";
// Intracluster connections
"distributed::DataManager" -> "distributed::DataRpcClients"
[arrowhead=vee];
"storage::RecordAccessor" -> "distributed::DataManager"
[style=dashed arrowhead=vee];
}
subgraph cluster_dynamic_partitioning {
label="Dynamic Partitioning" fontsize=14;
node [style=filled, color="#720096" fillcolor="#720096"];
// Owned Elements
"DynamicPartitioner";
}
subgraph cluster_security {
label="Security" fontsize=14;
node [style=filled, color="#857F87" fillcolor="#857F87"];
// Owned Elements
"Communication Encryption";
"Data Encryption";
"Access Control";
"Audit Logging";
}
subgraph cluster_web_dashboard {
label="Dashaboard" fontsize=14;
node [style=filled, color="#FF0092" fillcolor="#FF0092"];
// Owned Elements
"Memgraph Ops / Memgraph Cockpit";
}
subgraph cluster_rpc {
label="RPC" fontsize=14;
node [style=filled, color="#857F87" fillcolor="#857F87"];
// Owned Elements
"communication::rpc::Server";
"communication::rpc::Client";
}
subgraph cluster_ingestion {
label="Ingestion" fontsize=14;
node [style=filled, color="#0B6D88" fillcolor="#0B6D88"];
// Owned Elements
"Extract";
"Transform";
"Load";
"Amazon S3";
"Kafka";
// Intracluster connections
"Extract" -> "Amazon S3";
"Extract" -> "Kafka";
// Explicit positioning
{rank=same;"Extract";"Transform";"Load";}
}
// -- Intercluster connections --
// cluster_tcp_end_client_communication -- cluster_bolt_server
"communication::Server" -> "communication::bolt::SessionData" [color=black];
"communication::Server" -> "communication::bolt::Session" [color=black];
// cluster_bolt_server -> cluster_storage
"communication::bolt::SessionData" -> "database::GraphDb" [color=red];
"communication::bolt::Session" -> "database::GraphDbAccessor" [color=red];
// cluster_bolt_server -> cluster_opencypher_engine
"communication::bolt::SessionData" -> "query::Interpreter" [color=red];
// cluster_opencypher_engine -- cluster_storage
"query::Interpreter" -> "database::GraphDbAccessor" [color=black];
"query::Interpreter" -> "storage::VertexAccessor" [color=black];
"query::Interpreter" -> "storage::EdgeAccessor" [color=black];
"query::TypedValue" -> "storage::VertexAccessor" [color=black];
"query::TypedValue" -> "storage::EdgeAccessor" [color=black];
"query::Path" -> "storage::VertexAccessor"
[label="vertices" dir=both arrowtail=diamond arrowhead=vee color=black];
"query::Path" -> "storage::EdgeAccessor"
[label="edges" dir=both arrowtail=diamond arrowhead=vee color=black];
"query::plan::LogicalOperator" -> "database::GraphDbAccessor"
[color=black arrowhead=vee];
// cluster_distributed -- cluster_storage
"distributed::DataManager" -> "database::GraphDb"
[arrowhead=vee style=dashed color=red];
"distributed::DataManager" -> "tx::TransactionId"
[label="ves_caches_key" dir=both arrowhead=none arrowtail=diamond
color=red];
"distributed::DataManager" -> "storage::Vertex"
[label="vertices_caches" dir=both arrowhead=none arrowtail=diamond
color=red];
"distributed::DataManager" -> "storage::Edge"
[label="edges_caches" dir=both arrowhead=none arrowtail=diamond
color=red];
// cluster_storage -- cluster_properties_on_disk
"PropertyValueStore" -> "storage::KVStore"
[label="static" arrowhead=vee color=black];
// cluster_dynamic_partitioning -- cluster_storage
"database::GraphDb" -> "DynamicPartitioner"
[arrowhead=vee color=red];
"DynamicPartitioner" -> "database::GraphDbAccessor"
[arrowhead=vee color=black];
}

View File

@@ -1,22 +0,0 @@
digraph {
// label="Dynamig Graph Partitioning";
fontname="Roboto Bold"; fontcolor=black;
fontsize=26; labelloc=top; labeljust=center;
compound=true; // If true, allow edges between clusters
rankdir=TB; // Alternatives: LR
node [shape=record fontname="Roboto", fontsize=12, fontcolor=white
style=filled, color="#FB6E00" fillcolor="#FB6E00"];
edge [color="#B5AFB7"];
"distributed::DistributedGraphDb" -> "distributed::TokenSharingRpcServer";
"distributed::TokenSharingRpcServer" -> "communication::rpc::Server";
"distributed::TokenSharingRpcServer" -> "distributed::Coordination";
"distributed::TokenSharingRpcServer" -> "distributed::TokenSharingRpcClients";
"distributed::TokenSharingRpcServer" -> "distributed::dgp::Partitioner";
"distributed::dgp::Partitioner" -> "distributed::DistributedGraphDb" [style=dashed];
"distributed::dgp::Partitioner" -> "distributed::dgp::VertexMigrator";
"distributed::dgp::VertexMigrator" -> "database::GraphDbAccessor" [style=dashed];
}

View File

@@ -1,43 +0,0 @@
# Distributed addressing
In distributed Memgraph a single graph element must be owned by exactly
one worker. It is possible that multiple workers have cached copies of
a single graph element (which is inevitable), but there is only one
owner.
The owner of a graph element can change. This is not yet implemented,
but is intended. Graph partitioning is intended to be dynamic.
Graph elements refer to other graph elements that are possibly on some
other worker. Even though each graph element is identified with a unique
ID, that ID does not contain the information about where that element
currently resides (which worker is the owner).
Thus we introduce the concept of a global address. It indicates both
which graph element is referred to (it's global ID), and where it
resides. Semantically it's a pair of two elements, but for efficiency
it's stored in 64 bits.
The global address is efficient for usage in a cluster: it indicates
where something can be found. However, finding a graph element based on
it's ID is still not a free operation (in the current implementation
it's a skiplist lookup). So, whenever possible, it's better to use local
addresses (pointers).
Succinctly, the requirements for addressing are:
- global addressing containing location info
- fast local addressing
- storage of both types in the same location efficiently
- translation between the two
The `storage::Address` class handles the enumerated storage
requirements. It stores either a local or global address in the size of
a local pointer (typically 8 bytes).
Conversion between the two is done in multiple places. The general
approach is to use local addresses (when possible) only for local
in-memory handling. All the communication and persistence uses global
addresses. Also, when receiving address from another worker, attempt to
localize addresses as soon as possible, so that least code has to worry
about potential inefficiency of using a global address for a local graph
element.

View File

@@ -1,50 +0,0 @@
# Distributed durability
Durability in distributed is slightly different then in single-node as
the state itself is shared between multiple workers and none of those
states are independent.
Note that recovering from persistent storage must result in a stable
database state. This means that across the cluster the state
modification of every transaction that was running is either recovered
fully or not at all. Also, if transaction A committed before transaction B,
then if B is recovered so must A.
## Snapshots
It is possibly avoidable but highly desirable that the database can be
recovered from snapshot only, without relying on WAL files. For this to
be possible in distributed, it must be ensured that the same
transactions are recovered on all the workers (including master) in the
cluster. Since the snapshot does not contain information about which
state change happened in which transaction, the only way to achieve this
is to have synchronized snapshots. This means that the process of
creating a snapshot, which is in itself transactional (it happens within
a transaction and thus observes some consistent database state), must
happen in the same transaction. This is achieved by the master starting
a snapshot generating transaction and triggering the process on all
workers in the cluster.
## WAL
Unlike the snapshot, write-ahead logs contain the information on which
transaction made which state change. This makes it possible to include
or exclude transactions during the recovery process. What is necessary
however is a global consensus on which of the transactions should be
recovered and which not, to ensure recovery into a consistent state.
It would be possible to achieve this with some kind of synchronized
recovery process, but it would impose constraints on cluster startup and
would not be trivial.
A simpler alternative is that the consensus is achieved beforehand,
while the database (to be recovered) is still operational. What is
necessary is to keep track of which transactions are guaranteed to
have been flushed to the WAL files on all the workers in the cluster. It
makes sense to keep this record on the master, so a mechanism is
introduced which periodically pings all the workers, telling them to
flush their WALs, and writes some sort of a log indicating that this has
been confirmed. The downside of this is a periodic broadcast must be
done, and that potentially slightly less data can be recovered in the
case of a crash then if using a post-crash consensus. It is however much
simpler to implement.

View File

@@ -1,51 +0,0 @@
## Dynamic Graph Partitioning
Memgraph supports dynamic graph partitioning similar to the Spinner algorithm,
mentioned in this paper: [https://arxiv.org/pdf/1404.3861.pdf].
Dgp is useful because it tries to group `local` date on the same worker, i.e.
it tries to keep closely connected data on one worker. It tries to avoid jumps
across workers when querying/traversing the distributed graph.
### Our implementation
It works independently on each worker but it is always running the migration
on only one worker at the same time. It achieves that by sharing a token
between workers, and the token ownership is transferred to the next worker
when the current worker finishes its migration step.
The reason that we want workers to work in disjoint time slots is it avoid
serialization errors caused by creating/removing edges of vertices during
migrations, which might cause an update of some vertex from two or more
different transactions.
### Migrations
For each vertex and workerid (label in the context of Dgp algorithm) we define
a score function. Score function takes into account labels of surrounding
endpoints of vertex edges (in/out) and the capacity of the worker with said
label. Score function loosely looks like this
```
locality(v, l) =
count endpoints of edges of vertex `v` with label `l` / degree of `v`
capacity(l) =
number of vertices on worker `l` divided by the worker capacity
(usually equal to the average number of vertices per worker)
score(v, l) = locality(v, l) - capacity(l)
```
We also define two flags alongside ```dynamic_graph_partitioner_enabled```,
```dgp_improvement_threshold``` and ```dgp_max_batch_size```.
These two flags are used during the migration phase.
When deciding if we need to migrate some vertex `v` from worker `l1` to worker
`l2` we examine the difference in scores, i.e.
if score(v, l1) - dgp_improvement_threshold / 100 < score(v, l2) then we
migrate the vertex.
Max batch size flag limits the number of vertices we can transfer in one batch
(one migration step).
Setting this value to a too large value will probably cause
a lot of interference with client queries, and having it a small value
will slow down convergence of the algorithm.

View File

@@ -1,54 +0,0 @@
# Memgraph distributed
This chapter describes some of the concepts used in distributed
Memgraph. By "distributed" here we mean the sharding of a single graph
onto multiple processing units (servers).
## Conceptual organization
There is a single master and multiple workers. The master contains all
the global sources of truth (transaction engine,
[label|edge-type|property] to name mappings). Also, in the current
organization it is the only one that contains a Bolt server (for
communication with the end client) and an interpretation engine. Workers
contain the data and means of subquery interpretation (query plans
recieved from the master) and means of communication with the master and
other workers.
In many query plans the load on the master is much larger then the load
on the workers. For that reason it might be beneficial to make the
master contain less data (or none at all), and/or having multiple
interpretation masters.
## Logic organization
Both the distributed and the single node Memgraph use the same codebase.
In cases where the behavior in single-node differs from that in
distributed, some kind of dynamic behavior change is implemented (either
through inheritance or conditional logic).
### GraphDb
The `database::GraphDb` is an "umbrella" object for parts of the
database such as storage, garbage collection, transaction engine etc.
There is a class heirarchy of `GraphDb` implementations, as well as a
base interface object. There are subclasses for single-node, master and
worker deplotyments. Which implementation is used depends on the
configuration processed in the `main` entry point of memgraph.
The `GraphDb` interface exposes getters to base classes of
other similar heirarchies (for example to `tx::Engine`). In that way
much of the code that uses those objects (for example query plan
interpretation) is agnostic to the type of deployment.
### RecordAccessors
The functionality of `RecordAccessors` and it's subclasses is already
documented. It's important to note that the same implementation of
accessors is used in all deployments, with internal changes of behavior
depending on the locality of the graph element (vertex or edge) the
accessor represents. For example, if the graph element is local, an
update operation on an accessor will make the necessary MVCC ops, update
local data, indexes, the write-ahead log etc. However, if the accessor
represents a remote graph element, an update will trigger an RPC message
to the owner about the update and a change in the local cache.

View File

@@ -1,103 +0,0 @@
# Distributed updates
Operations that modify the graph state are somewhat more complex in the
distributed system, as opposed to a single-node Memgraph deployment. The
complexity arises from two factors.
First, the data being modified is not necessarily owned by the worker
performing the modification. This situation is completely valid workers
execute parts of the query plan and parts must be executed by the
master.
Second, there are less guarantees regarding multi-threaded access. In
single-node Memgraph it was guaranteed that only one transaction will be
performing database work in a single transaction. This implied that
per-version storage could be thread-unsafe. In distributed Memgraph it
is possible that multiple threads could be performing work in the same
transaction as a consequence of the query being executed at the same
time on multiple workers and those executions interacting with the
globally partitioned database state.
## Deferred state modification
Making the per-version data storage thread-safe would most likely have a
performance impact very undesirable in a transactional database intended
for high throughput.
An alternative is that state modification over unsafe structures is not
performed immediately when requested, but postponed until it is safe to
do (there is is a guarantee of no concurrent access).
Since local query plan execution is done the same way on local data as
it is in single-node Memgraph, it is not possible to deffer that part of
the modification story. What can be deferred are modifications requested
by other workers. Since local query plan execution still is
single-threaded, this approach is safe.
At the same time those workers requesting the remote update can update
local copies (caches) of the not-owned data since that cache is only
being used by the single, local-execution thread.
### Visibility
Since updates are deferred the question arises: when do the updates
become visible? The above described process offers the following
visibility guarantees:
- updates done on the local state are visible to the owner
- updates done on the local state are NOT visible to anyone else during
the same (transaction + command)
- updates done on remote state are deferred on the owner and not
visible to the owner until applied
- updates done on the remote state are applied immediately to the local
caches and thus visible locally
This implies an inconsistent view of the database state. In a concurrent
execution of a single query this can hardly be avoided and is accepted
as such. It does not change the Cypher query execution semantic in any
of the well-defined scenarios. It possibly changes some of the behaviors
in which the semantic is not well defined even in single-node execution.
### Synchronization, update application
In many queries it is mandatory to observe the latest global graph state
(typically when returning it to the client). That means that before that
happens all the deferred updates need to be applied, and all the caches
to remote data invalidated. Exactly this happens when executing queries
that modify the graph state. At some point a global synchronization
point is reached. First it is waited that all workers finish the
execution of query plan parts performing state modifications. After that
all the workers are told to apply the deferred updates they received to
their graph state. Since there is no concurrent query plan execution,
this is safe. Once that is done all the local caches are cleared and the
requested data can be returned to the client.
### Command advancement
In complex queries where a read part follows a state modification part
the synchronization process after the state modification part is
followed by command advancement, like in single-node execution.
## Creation
Graph element creation is not deferred. This is practical because the
response to a creation is the global ID of the newly created element. At
the same time it is safe because no other worker (including the owner)
will be using the newly added graph element.
## Updating
Updating is deferred, as described. Note that this also means that
record locking conflicts are deferred and serialization errors
(including lock timeouts) are postponed until the deferred update
application phase. In certain scenarios it might be beneficial to force
these errors to happen earlier, when the deferred update request is
processed.
## Deletion
Deletion is also deferred. Deleting an edge implies a modification of
it's endpoint vertices, which must be deferred as those data structures
are not thread-safe. Deleting a vertex is either with detaching, in
which case an arbitrary number of updates are implied in the vertex's
neighborhood, or without detaching which relies on checking the current
state of the graph which is generally impossible in distributed.

View File

@@ -1,22 +0,0 @@
# Snapshots
A "snapshot" is a record of the current database state stored in permanent
storage. Note that the term "snapshot" is used also in the context of
the transaction engine to denote a set of running transactions.
A snapshot is written to the file by Memgraph periodically if so
configured. The snapshot creation process is done within a transaction created
specifically for that purpose. The transaction is needed to ensure that
the stored state is internally consistent.
The database state can be recovered from the snapshot during startup, if
so configured. This recovery works in conjunction with write-ahead log
recovery.
A single snapshot contains all the data needed to recover a database. In
that sense snapshots are independent of each other and old snapshots can
be deleted once the new ones are safely stored, if it is not necessary
to revert the database to some older state.
The exact format of the snapshot file is defined inline in the snapshot
creation code.

View File

@@ -1,16 +0,0 @@
#!/bin/bash
script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
mkdir -p $script_dir/html
for markdown_file in $(find $script_dir -name '*.md'); do
name=$(basename -s .md $markdown_file)
sed -e 's/.md/.html/' $markdown_file | \
pandoc -s -f markdown -t html -o $script_dir/html/$name.html
done
for dot_file in $(find $script_dir -name '*.dot'); do
name=$(basename -s .dot $dot_file)
dot -Tpng $dot_file -o $script_dir/html/$name.png
done

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@@ -31,14 +31,6 @@ the following steps.
After the generation, the execution cost of each plan is estimated. This
estimation is used to select the best plan which will be executed.
5. [Distributed Planning](#distributed-planning)
In case we are running distributed Memgraph, the final plan is adapted
for distributed execution. NOTE: This appears to be an error in the
workflow. Distributed planning should be moved before step 3. or
integrated with it. With the workflow ordered as is now, cost estimation
doesn't consider the distributed plan.
The implementation can be found in the `query/plan` directory, with the public
entry point being `query/plan/planner.hpp`.
@@ -476,12 +468,59 @@ finally for this example, the `Merge` would have:
## Logical Plan Postprocessing
NOTE: TODO
Postprocessing of a logical plan is done by rewriting the original plan into
a more efficient one while preserving the original semantic of operations.
The rewriters are found in `query/plan/rewrite` directory, and currently we
only have one -- `IndexLookupRewriter`.
### IndexLookupRewriter
The job of this rewriter is to merge `Filter` and `ScanAll` operations into
equivalent `ScanAllBy<Index>` operations. In almost all cases using indexed
lookup will be faster than regular lookup, so `IndexLookupRewriter` simply
does the transformations whenever possible. The simplest case being the
following, assuming we have an index over `id`.
* Original Plan
`ScanAll (n) > Filter (id(n) == 42) > Produce (n)`
* Rewritten Plan
`ScanAllById (n, id=42) > Produce (n)`
Naturally, there are some cases we need to be careful about.
1. Operators with Multiple Branches
Here we may not carry `Filter` operations outside of the operator into
its branches, so the branches are rewritten as stand alone plans with a
branch new `IndexLookupRewriter`. Some of the operators with multiple
branches are `Merge`, `Optional`, `Cartesian` and `Union`.
2. Expand Operators
Expand operations aren't that tricky to handle, but they have a special
case where we want to use an indexed lookup of the destination so that the
expansion is performed between known nodes. This decision may depend on
various parameters which may need further tweaking as we encounter more
use-cases of Cypher queries.
## Cost Estimation
NOTE: TODO
Cost estimation is the final step of processing a logical plan. The
implementation can be found in `query/plan/cost_estimator.hpp`. We give each
operator a cost based on the estimated cardinality of results of that operator
and on the preset coefficient of the runtime performance of that operator.
## Distributed Planning
This scheme is rather simple and works quite well, but there are couple of
improvements we may want to do at some point.
NOTE: TODO
* Track more information about the stored graph and use that to improve the
estimates.
* Do a quick, partial run of the plan and tweak the estimation based on how
much each operator produced results. This may require us having some kind
of representative subset of the stored graph.
* Write micro benchmarks for each operator and based on the results create
sensible preset coefficients. This would replace the current coefficients
which are just assumptions on how each operator implementation performs.

View File

@@ -10,14 +10,12 @@ on your machine before you can download the source code.
On Debian systems, you can do it inside a terminal with the following
command:
sudo apt-get install git
On ArchLinux or Gentoo, you probably already know what to do.
apt install git
After installing `git`, you are now ready to fetch your own copy of Memgraph
source code. Run the following command:
git clone https://phabricator.memgraph.io/diffusion/MG/memgraph.git
git clone https://github.com/memgraph/memgraph.git
The above will create a `memgraph` directory and put all source code there.
@@ -32,19 +30,21 @@ In your terminal, position yourself in the obtained memgraph directory.
### Installing Dependencies
On Debian systems, dependencies that are required by the codebase should be
setup by running the `init` script:
./init -s
Currently, other systems aren't supported in the `init` script. But you can
issue the needed steps manually. First run the `init` script.
Dependencies that are required by the codebase should be checked by running the
`init` script:
./init
The script will output the required packages, which you should be able to
install via your favorite package manager. For example, `pacman` on ArchLinux.
After installing the packages, issue the following commands:
If the script fails, dependencies installation scripts could be found under
`environment/os/`. The directory contains dependencies management script for
each supported operating system. E.g. if your system is **Debian 10**, run the
following to install all required build packages:
./environment/os/debian-10.sh install MEMGRAPH_BUILD_DEPS
Once everything is installed, rerun the `init` script.
Once the `init` script is successfully finished, issue the following commands:
mkdir -p build
./libs/setup.sh
@@ -52,18 +52,17 @@ After installing the packages, issue the following commands:
### Compiling
Memgraph is compiled using our own custom toolchain that can be obtained from
[Toolchain repository](https://deps.memgraph.io/toolchain). You should read
the `README.txt` file in the repository and install the apropriate toolchain
for your distribution. After you have installed the toolchain you should read
the instructions for the toolchain in the toolchain install directory
(`/opt/toolchain-vXYZ/README.md`) and install dependencies that are necessary
to run the toolchain.
the toolchain repository. You should read the `environment/README.txt` file
in the repository and install the apropriate toolchain for your distribution.
After you have installed the toolchain you should read the instructions for the
toolchain in the toolchain install directory (`/opt/toolchain-vXYZ/README.md`)
and install dependencies that are necessary to run the toolchain.
When you want to compile Memgraph you should activate the toolchain using the
prepared toolchain activation script that is also described in the toolchain
`README`.
NOTE: You *must* activate the toolchain every time you want to compile
NOTE: You **must** activate the toolchain every time you want to compile
Memgraph!
You should now activate the toolchain in your console.

View File

@@ -38,7 +38,7 @@ interpretation code) should work with accessors. There is a
`RecordAccessor` as a base class for `VertexAccessor` and
`EdgeAccessor`. Following is an enumeration of their purpose.
### Data access
### Data Access
The client interacts with Memgraph using the Cypher query language. That
language has certain semantics which imply that multiple versions of the
@@ -75,7 +75,7 @@ In distributed Memgraph accessors also contain a lot of the remote graph
element handling logic. More info on that is available in the
documentation for distributed.
### Deferred MVCC data lookup for Edges
### Deferred MVCC Data Lookup for Edges
Vertices and edges are versioned using MVCC. This means that for each
transaction an MVCC lookup needs to be done to determine which version
@@ -100,7 +100,7 @@ working with that data directly whenever possible! Always consider the
accessors to be the first go-to for interacting with data, especially
when in the context of a transaction.
# Skiplist accessor
# Skiplist Accessor
The term "accessor" is also used in the context of a skiplist. Every
operation on a skiplist must be performed within on an

View File

@@ -0,0 +1,6 @@
# Storage v1
* [Accessors](accessors.md)
* [Indexes](indexes.md)
* [Property Storage](property-storage.md)
* [Durability](durability.md)

View File

@@ -1,4 +1,6 @@
# Write-ahead logging
# Durability
## Write-ahead Logging
Typically WAL denotes the process of writing a "log" of database
operations (state changes) to persistent storage before committing the
@@ -53,3 +55,26 @@ inefficient) to recover the database from WAL only, provided all the WAL
files created from DB start are available. It is not possible to recover
partial database state (i.e. from some suffix of WAL files, without the
preceding snapshot).
## Snapshots
A "snapshot" is a record of the current database state stored in permanent
storage. Note that the term "snapshot" is used also in the context of
the transaction engine to denote a set of running transactions.
A snapshot is written to the file by Memgraph periodically if so
configured. The snapshot creation process is done within a transaction created
specifically for that purpose. The transaction is needed to ensure that
the stored state is internally consistent.
The database state can be recovered from the snapshot during startup, if
so configured. This recovery works in conjunction with write-ahead log
recovery.
A single snapshot contains all the data needed to recover a database. In
that sense snapshots are independent of each other and old snapshots can
be deleted once the new ones are safely stored, if it is not necessary
to revert the database to some older state.
The exact format of the snapshot file is defined inline in the snapshot
creation code.

View File

@@ -1,10 +1,10 @@
# Label indexes
# Label Indexes
These are unsorted indexes that contain all the vertices that have the label
the indexes are for (one index per label). These kinds of indexes get
automatically generated for each label used in the database.
### Updating the indexes
### Updating the Indexes
Whenever something gets added to the record we update the index (add that
record to index). We keep an index which might contain garbage (not relevant
@@ -64,7 +64,7 @@ same order, with (record, vlist) pair
already superseded by a newer record and as such won't be inserted while
it's being deleted
### Querying the index
### Querying the Index
We run through the index for the given label and do `vlist.find` operation for
the current transaction, and check if the newest return record has that
@@ -75,7 +75,7 @@ in the index are sorted by their `vlist*` and as such we can filter consecutive
duplicate `vlist*` to only return one of those while still being able to create
an iterator to index.
### Cleaning the index
### Cleaning the Index
Cleaning the index is not as straightforward as it seems as a lot of garbage
can accumulate, but it's hard to know when exactly can we delete some (record,

View File

@@ -1,7 +1,7 @@
# Property storage
# Property Storage
Although the reader is probably familiar with properties in *Memgraph*, let's
briefly recap.
briefly recap.
Both vertices and edges can store an arbitrary number of properties. Properties
are, in essence, ordered pairs of property names and property values. Each
@@ -21,9 +21,9 @@ must be one of the following types:
Property values are modeled in a class conveniently called `PropertyValue`.
## Mapping between property names and property keys.
## Mapping Between Property Names and Property Keys.
Although users think of property names in terms of descriptive strings
Although users think of property names in terms of descriptive strings
(e.g. "location" or "department"), *Memgraph* internally converts those names
into property keys which are, essentially, unsigned 16-bit integers.
@@ -49,7 +49,7 @@ An interface of `PropertyValueStore` is as follows:
`clear` | Clears the storage.
`iterator`| Provides an extension of `std::input_iterator` that iterates over storage.
## Storage location
## Storage Location
By default, *Memgraph* is an in-memory database and all properties are therefore
stored in working memory unless specified otherwise by the user. User has an
@@ -63,12 +63,12 @@ property key has the following format:
```
|---location--|------id------|
|-Memory|Disk-|-----2^15-----|
```
```
In other words, the most significant bit determines the location where the
property will be stored.
property will be stored.
### In-memory storage
### In-memory Storage
The underlying implementation of in-memory storage for the time being is
`std::vector<std::pair<Property, PropertyValue>>`. Implementations of`at`, `set`
@@ -76,7 +76,7 @@ and `erase` are linear in time. This implementation is arguably more efficient
than `std::map` or `std::unordered_map` when the average number of properties of
a record is relatively small (up to 10) which seems to be the case.
### On-disk storage
### On-disk Storage
#### KVStore
@@ -120,12 +120,12 @@ KVStore storage = ...;
for (auto it = storage.begin("alpha"); it != storage.end("omega"); ++it) {}
```
#### Data organization on disk
#### Data Organization on Disk
Each `PropertyValueStore` instance can access a static `KVStore` object that can
store `(key, value)` pairs on disk. The key of each property on disk consists of
two parts &mdash; a unique identifier (unsigned 64-bit integer) of the current
record version (see mvcc docummentation for further clarification) and a
record version (see mvcc docummentation for further clarification) and a
property key as described above. The actual value of the property is serialized
into a bytestring using bolt `BaseEncoder`. Similarly, deserialization is
performed by bolt `Decoder`.

View File

@@ -0,0 +1,3 @@
# Storage v2
TODO(gitbuda): Write documentation.

View File

@@ -1,152 +0,0 @@
# Bootstrapping Compilation Toolchain for Memgraph
Requirements:
* libstdc++ shipped with gcc-6.3 or gcc-6.4
* cmake >= 3.1, Debian Stretch uses cmake-3.7.2
* clang-3.9
## Installing gcc-6.4
gcc-6.3 has a bug, so use the 6.4 version which is just a bugfix release.
Requirements on CentOS 7:
* wget
* make
* gcc (bootstrap)
* gcc-c++ (bootstrap)
* gmp-devel (bootstrap)
* mpfr-devel (bootstrap)
* libmpc-devel (bootstrap)
* zip
* perl
* dejagnu (testing)
* expect (testing)
* tcl (testing)
```
wget ftp://ftp.mpi-sb.mpg.de/pub/gnu/mirror/gcc.gnu.org/pub/gcc/releases/gcc-6.4.0/gcc-6.4.0.tar.gz
tar xf gcc-6.4.0.tar.gz
cd gcc-6.4.0
mkdir build
cd build
../configure --disable-multilib --prefix=<install-dst>
make
# Testing
make -k check
make install
```
*Do not put gcc + libs on PATH* (unless you know what you are doing).
## Installing cmake-3.7.2
Requirements on CentOS 7:
* wget
* make
* gcc
* gcc-c++
* ncurses-devel (optional, for ccmake)
```
wget https://cmake.org/files/v3.7/cmake-3.7.2.tar.gz
tar xf cmake-3.7.2.tar.gz
cd cmake-3.7.2.tar.gz
./bootstrap --prefix<install-dst>
make
make install
```
Put cmake on PATH (if appropriate)
**Fix the bug in CpackRPM**
`"<path-to-cmake>/share/cmake-3.7/Modules/CPackRPM.cmake" line 2273 of 2442`
The line
```
set(RPMBUILD_FLAGS "-bb")
```
needs to be before
```
if(CPACK_RPM_GENERATE_USER_BINARY_SPECFILE_TEMPLATE OR NOT CPACK_RPM_USER_BINARY_SPECFILE)
```
It was probably accidentally placed after, and is fixed in later cmake
releases.
## Installing clang-3.9
Requirements on CentOS 7:
* wget
* make
* cmake
```
wget http://releases.llvm.org/3.9.1/llvm-3.9.1.src.tar.xz
tar xf llvm-3.9.1.src.tar.xz
mv llvm-3.9.1.src llvm
wget http://releases.llvm.org/3.9.1/cfe-3.9.1.src.tar.xz
tar xf cfe-3.9.1.src.tar.xz
mv cfe-3.9.1.src llvm/tools/clang
cd llvm
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE="Release" -DGCC_INSTALL_PREFIX=<gcc-dir> \
-DCMAKE_C_COMPILER=<gcc> -DCMAKE_CXX_COMPILER=<g++> \
-DCMAKE_CXX_LINK_FLAGS="-L<gcc-dir>/lib64 -Wl,-rpath,<gcc-dir>/lib64" \
-DCMAKE_INSTALL_PREFIX=<install-dst> ..
make
# Testing
make check-clang
make install
```
Put clang on PATH (if appropriate)
## Memgraph
Requirements on CentOS 7:
* libuuid-devel (antlr4)
* java-1.8.0-openjdk (antlr4)
* boost-static (too low version --- compile manually)
* rpm-build (RPM)
* python3 (tests, ...)
* which (required for rocksdb)
* sbcl (lisp C++ preprocessing)
### Boost 1.62
```
wget https://netix.dl.sourceforge.net/project/boost/boost/1.62.0/boost_1_62_0.tar.gz
tar xf boost_1_62_0.tar.gz
cd boost_1_62_0
./bootstrap.sh --with-toolset=clang --with-libraries=iostreams,serialization --prefix=<install-dst>
./b2
# Default installs to /usr/local/
./b2 install
```
### Building Memgraph
clang is *required* to be findable by cmake, i.e. it should be on PATH.
cmake isn't required to be on the path, since you run it manually, so can use
the full path to executable in order to run it. Obviously, it is convenient to
put cmake also on PATH.
Building is done as explained in [Quick Start](quick-start.md), but each
`make` invocation needs to be prepended with:
`LD_RUN_PATH=<gcc-dir>/lib64 make ...`
### RPM
Name format: `memgraph-<version>-<pkg-version>.<arch>.rpm`

View File

@@ -20,54 +20,51 @@ Finally, make git aware of your favourite editor:
git config --global core.editor "vim"
## Phabricator
## Github
All of the code in Memgraph needs to go through code review before it can be
accepted in the codebase. This is done through
[Phabricator](https://phacility.com/phabricator/). The command line tool for
interfacing with Phabricator is
[arcanist](https://phacility.com/phabricator/arcanist/). You should already
have it installed if you followed the steps in [Quick Start](quick-start.md).
The only required setup is to go in the root of Memgraph's project and run:
arc install-certificate
accepted in the codebase. This is done through [Github](https://github.com/).
You should already have it installed if you followed the steps in [Quick
Start](quick-start.md).
## Working on Your Feature Branch
Git has a concept of source code *branches*. The `master` branch contains all
Git has a concept of source code **branches**. The `master` branch contains all
of the changes which were reviewed and accepted in Memgraph's code base. The
`master` branch is selected by default.
### Creating a Branch
When working on a new feature or fixing a bug, you should create a new branch
out of the `master` branch. For example, let's say you are adding static type
checking to the query language compiler. You would create a branch called
`mg_query_static_typing` with the following command:
out of the `master` branch. There are two branch types, **epic** and **task**
branches. The epic branch is created when introducing a new feature or any work
unit requiring more than one commit. More commits are required to split the
work into chunks to be able to easier review code or find a bug (in each
commit, there could be various problems, e.g., related to performance or
concurrency issues, which are the hardest to track down). Each commit on the
master or epic branch should be a compilable and well-documented set of
changes. Task branches should be created when a smaller work unit has to be
integrated into the codebase. The task branch could be branched out of the
master or an epic branch. We manage epics and tasks on the project management
tool called [Airtable](https://airtable.com/tblTUqycq8sHTTkBF). Each epic is
prefixed by `Exyz-MG`, on the other hand, each task has `Tabcd-MG` prefix.
Examples on how to create branches follow:
git branch mg_query_static_typing
To switch to that branch, type:
git checkout mg_query_static_typing
Since doing these two steps will happen often, you can use a shortcut command:
git checkout -b mg_query_static_typing
```
git checkout master
git checkout -b T0025-MG-fix-a-problem
...
git checkout master
git checkout -b E025-MG-huge-feature
...
git checkout E025-MG-huge-feature
git checkout -b T0123-MG-add-feature-part
```
Note that a branch is created from the currently selected branch. So, if you
wish to create another branch from `master` you need to switch to `master`
first.
The usual convention for naming your branches is `mg_<feature_name>`, you may
switch underscores ('\_') for hyphens ('-').
Do take care not to mix the case of your branch names! Certain operating
systems (like Windows) don't distinguish the casing in git branches. This may
cause hard to track down issues when trying to switch branches. Therefore, you
should always name your branches with lowercase letters.
### Making and Committing Changes
When you have a branch for your new addition, you can now actually start
@@ -98,80 +95,72 @@ possible.
### Sending Changes on a Review
After finishing your work on your feature branch, you will want to send it on
code review. This is done through Arcanist. To do that, run the following
command:
code review. This is done by pushing the branch to Github and creating a pull
request. You can find all PRs
[here](https://github.com/memgraph/memgraph/pulls).
arc diff
### Code Integration
You will, once again, be presented with an editor where you need to describe
your whole work. `arc` will by default fill that description with your commit
messages. The title and summary of your work should also follow the
conventions of git messages as described above. If you followed the
guidelines, the message filled by `arc` should be fine.
When working, you have to integrate some changes to your work or push your work
to be available for others. To pull changes into a local `branch`, usually run
the following:
In addition to the message, you need to fill the `Reviewers:` line with
usernames of people who should do the code review.
git checkout {{branch}}
git pull origin {{branch}}
You changes will be visible on Phabricator as a so called "diff". You can find
the default view of active diffs
[here](https://phabricator.memgraph.io/differential/)
To push your changes, usually run the following:
### Updating Changes Based on Review
git checkout {{branch}}
git push origin {{branch}}
When you get comments in the code review, you will want to make additional
modifications to your work. The same workflow as before applies: [Making and
Committing Changes](#making-and-committing-changes)
Sometimes, things could get a little bit more complicated. Diagram below shows
which git operation should be performed if a piece of code has to be integrated
from one branch to another. Note, `main_branch` is the **master** branch in our
case.
After making those changes, send them back on code review:
```
|<---------------------------|
| squash merge |
|--------------------------->|
| merge |
| |
|<-----------|<--------------|
| merge | squash merge |
| | |
|----------->|-------------->|
| rebase | merge |
| | rebase --onto |
| | |
main_branch epic_branch task_branch
```
arc diff
There are a couple of cases:
* If a code has to be integrated from a task branch to the main branch, use
**squash merge**. While you were working on a task, you probably committed a
couple of cleanup commits that are not relevant to the main branch. In the
other direction, while integrating the main branch to a task branch, the
**regular merge** is ok because changes from the task branch will later be
squash merged.
### Updating From New Master
* You should use **squash merge** when integrating changes from task to epic
branch (task might have irrelevant commits). On the other hand, you should
use a **regular merge** when an epic is completed and has to be integrated into
the main branch. Epic is a more significant piece of work, decoupled in
compilable and testable commits. All these commits should be preserved to be
able to find potential issues later on.
Let's say that, while you were working, someone else added some new features
to the codebase that you would like to use in your current work. To obtain
those changes you should update your `master` branch:
* You should use **rebase** when integrating changes from main to an epic
branch. The epic branch has to be as clean as possible, avoid pure merge
commits. Once you rebase epic on main, all commits on the epic branch will
change the hashes. The implications are: 1) you have to force push your local
branch to the origin, 2) if you made a task branch out of the epic branch, you
would have to use **rebase --onto** (please refer to `git help rebase` for
details). In simple cases, **regular merge** should be sufficient to integrate
changes from epic to a task branch (that can even be done via GitHub web
interface).
git checkout master
git pull origin master
Now, these changes are on `master`, but you want them in your local branch. To
do that, use `git rebase`:
git checkout mg_query_static_typing
git rebase master
During `git rebase`, you may get reports that some files have conflicting
changes. If you need help resolving them, don't be afraid to ask around! After
you've resolved them, mark them as done with `git add` command. You may
then continue with `git rebase --continue`.
After the `git rebase` is done, you will now have new changes from `master` on
your feature branch as if you just created and started working on that branch.
You may continue with the usual workflow of [Making and Committing
Changes](#making-and-committing-changes) and [Sending Changes on a
Review](#sending-changes-on-a-review).
### Sending Your Changes on Master Branch
When your changes pass the code review, you are ready to integrate them in the
`master` branch. To do that, run the following command:
arc land
Arcanist will take care of obtaining the latest changes from `master` and
merging your changes on top. If the `land` was successful, Arcanist will
delete your local branch and you will be back on `master`. Continuing from the
examples above, the deleted branch would be `mg_query_static_typing`.
This marks the completion of your changes, and you are ready to work on
something else.
### Note For People Familiar With Git
Since Arcanist takes care of merging your git commits and pushing them on
`master`, you should *never* have to call `git merge` and `git push`. If you
find yourself typing those commands, check that you are doing the right thing.
The most common mistake is to use `git merge` instead of `git rebase` for the
case described in [Updating From New Master](#updating-from-new-master).
During any code integration, you may get reports that some files have
conflicting changes. If you need help resolving them, don't be afraid to ask
around! After you've resolved them, mark them as done with `git add` command.
You may then continue with `git {{action}} --continue`.

View File

@@ -1,7 +1,6 @@
# Memgraph Code Documentation
IMPORTANT: auto-generated (run doxygen Doxyfile in the project root)
IMPORTANT: Auto-generated (run doxygen Doxyfile in the project root).
* HTML - just open docs/doxygen/html/index.html
* Latex - run make inside docs/doxygen/latex
* HTML - Open docs/doxygen/html/index.html.
* Latex - Run make inside docs/doxygen/latex.

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -1,20 +0,0 @@
## Dynamic Graph Partitioner
Memgraph supports dynamic graph partitioning which dynamically improves
performance on badly partitioned dataset over workers. To enable it, the user
should use the following flag when firing up the *master* node:
```plaintext
--dynamic_graph_partitioner_enable
```
### Parameters
| Name | Default Value | Description | Range |
|------|---------------|-------------|-------|
|--dgp_improvement_threshold | 10 | How much better should specific node score
be to consider a migration to another worker. This represents the minimal
difference between new score that the vertex will have when migrated and the
old one such that it's migrated. | Min: 1, Max: 100
|--dgp_max_batch_size | 2000 | Maximal amount of vertices which should be
migrated in one dynamic graph partitioner step. | Min: 1, Max: MaxInt32 |

View File

@@ -0,0 +1,3 @@
# Feature Specifications
* [Python Query Modules](python-query-modules.md)

View File

@@ -1,214 +1,275 @@
# High Availability (abbr. HA)
## Introduction
## High Level Context
High availability is a characteristic of a system which aims to ensure a
certain level of operational performance for a higher-than-normal period.
Although there are multiple ways to design highly available systems, Memgraph
strives to achieve HA by elimination of single points of failure. In essence,
this implies adding redundancy to the system so that a failure of a component
does not imply the failure of the entire system.
does not imply the failure of the entire system. To ensure this, HA Memgraph
implements the [Raft consensus algorithm](https://raft.github.io/).
## Theoretical Background
Correct implementation of the algorithm guarantees that the cluster will be
fully functional (available) as long as any strong majority of the servers are
operational and can communicate with each other and with clients. For example,
clusters of three or four machines can tolerate the failure of a single server,
clusters of five and six machines can tolerate the failure of any two servers,
and so on. Therefore, we strongly recommend a setup of an odd-sized cluster.
The following chapter serves as an introduction into some theoretical aspects
of Memgraph's high availability implementation. If the reader is solely
interested in design decisions around HA implementation, they can skip this
chapter.
### Performance Implications
An important implication of any HA implementation stems from Eric Brewer's
[CAP Theorem](https://fenix.tecnico.ulisboa.pt/downloadFile/1126518382178117/10.e-CAP-3.pdf)
which states that it is impossible for a distributed system to simultaneously
achieve:
Internally, Raft achieves high availability by keeping a consistent replicated
log on each server within the cluster. Therefore, we must successfully replicate
a transaction on the majority of servers within the cluster before we actually
commit it and report the result back to the client. This operation represents
a significant performance hit when compared with single node version of
Memgraph.
* Consistency (C - every read receives the most recent write or an error)
* Availability (A - every request receives a response that is not an error)
* Partition tolerance (P - The system continues to operate despite an
arbitrary number of messages being dropped by the
network between nodes)
Luckily, the algorithm can be tweaked in a way which allows read-only
transactions to perform significantly better than those which modify the
database state. That being said, the performance of read-only operations
is still not going to be on par with single node Memgraph.
In the context of HA, Memgraph should strive to achieve CA.
This section will be updated with exact numbers once we integrate HA with
new storage.
### Consensus
With the old storage, write throughput was almost five times lower than read
throughput (~30000 reads per second vs ~6000 writes per second).
Implications of the CAP theorem naturally lead us towards introducing a
cluster of machines which will have identical internal states. When a designated
machine for handling client requests fails, it can simply be replaced with
another.
## User Facing Setup
Well... turns out this is not as easy as it sounds :(
### How to Setup HA Memgraph Cluster?
Keeping around a cluster of machines with consistent internal state is an
inherently difficult problem. More precisely, this problem is as hard as
getting a cluster of machines to agree on a single value, which is a highly
researched area in distributed systems. Our research of state of the art
consensus algorithms lead us to Diego Ongaro's
[Raft algorithm](https://raft.github.io/raft.pdf).
First, the user needs to install `memgraph_ha` package on each machine
in their cluster. HA Memgraph should be available as a Debian package,
so its installation on each machine should be as simple as:
#### Raft
```plaintext
dpkg -i /path/to/memgraph_ha_<version>.deb
```
As you might have guessed, analyzing each subtle detail of Raft goes way
beyond the scope of this document. In the remainder of the chapter we will
outline only the most important ideas and implications, leaving all further
analysis to the reader. Detailed explanation can be found either in Diego's
[dissertation](https://ramcloud.stanford.edu/~ongaro/thesis.pdf) \[1\] or the
Raft [paper](https://raft.github.io/raft.pdf) \[2\].
After successful installation of the `memgraph_ha` package, the user should
finish its configuration before attempting to start the cluster.
In essence, Raft allows us to implement the previously mentioned idea of
managing a cluster of machines with identical internal states. In other
words, the Raft protocol allows us to manage a cluster of replicated
state machines which is fully functional as long as the *majority* of
the machines in the cluster operate correctly.
There are two main things that need to be configured on every node in order for
the cluster to be able to run:
Another important fact is that those state machines must be *deterministic*.
In other words, the same command on two different machines with the same
internal state must yield the same result. This is important because Memgraph,
as a black box, is not entirely deterministic. Non-determinism can easily be
introduced by the user (e.g. by using the `rand` function) or by algorithms
behind query execution (e.g. introducing fuzzy logic in the planner could yield
a different order of results). Luckily, once we enter the storage level,
everything should be fully deterministic.
1. The user has to edit the main configuration file and specify the unique node
ID to each server in the cluster
2. The user has to create a file that describes all IP addresses of all servers
that will be used in the cluster
To summarize, Raft is a protocol which achieves consensus in a cluster of
deterministic state machines via log replication. The cluster is fully
functional if the majority of the machines work correctly. The reader
is strongly encouraged to gain a deeper understanding (at least read through
the paper) of Raft before reading the rest of this document.
The `memgraph_ha` binary loads all main configuration parameters from
`/etc/memgraph/memgraph_ha.conf`. On each node of the cluster, the user should
uncomment the `--server-id=0` parameter and change its value to the `server_id`
of that node.
The last step before starting the server is to create a `coordination`
configuration file. That file is already present as an example in
`/etc/memgraph/coordination.json.example` and you have to copy it to
`/etc/memgraph/coordination.json` and edit it according to your cluster
configuration. The file contains coordination info consisting of a list of
`server_id`, `ip_address` and `rpc_port` lists. The assumed contents of the
`coordination.json` file are:
```plaintext
[
[1, "192.168.0.1", 10000],
[2, "192.168.0.2", 10000],
[3, "192.168.0.3", 10000]
]
```
Here, each line corresponds to coordination of one server. The first entry is
that server's ID, the second is its IP address and the third is the RPC port it
listens to. This port should not be confused with the port used for client
interaction via the Bolt protocol.
The `ip_address` entered for each `server_id` *must* match the exact IP address
that belongs to that server and that will be used to communicate to other nodes
in the cluster. The coordination configuration file *must* be identical on all
nodes in the cluster.
After the user has set the `server_id` on each node in
`/etc/memgraph/memgraph_ha.conf` and provided the same
`/etc/memgraph/coordination.json` file to each node in the cluster, they can
start the Memgraph HA service by issuing the following command on each node in
the cluster:
```plaintext
systemctl start memgraph_ha
```
### How to Configure Raft Parameters?
All Raft configuration parameters can be controlled by modifying
`/etc/memgraph/raft.json`. The assumed contents of the `raft.json` file are:
```plaintext
{
"election_timeout_min": 750,
"election_timeout_max": 1000,
"heartbeat_interval": 100,
"replication_timeout": 20000,
"log_size_snapshot_threshold": 50000
}
```
The meaning behind each entry is demystified in the following table:
Flag | Description
------------------------------|------------
`election_timeout_min` | Lower bound for the randomly sampled reelection timer given in milliseconds
`election_timeout_max` | Upper bound for the randomly sampled reelection timer given in milliseconds
`heartbeat_interval` | Time interval between consecutive heartbeats given in milliseconds
`replication_timeout` | Time interval allowed for data replication given in milliseconds
`log_size_snapshot_threshold` | Allowed number of entries in Raft log before its compaction
### How to Query HA Memgraph via Proxy?
This chapter describes how to query HA Memgraph using our proxy server.
Note that this is not intended to be a long-term solution. Instead, we will
implement a proper Memgraph HA client which is capable of communicating with
the HA cluster. Once our own client is implemented, it will no longer be
possible to query HA Memgraph using other clients (such as neo4j client).
The Bolt protocol that is exposed by each Memgraph HA node is an extended
version of the standard Bolt protocol. In order to be able to communicate with
the highly available cluster of Memgraph HA nodes, the client must have some
logic implemented in itself so that it can communicate correctly with all nodes
in the cluster. To facilitate a faster start with the HA cluster we will build
the Memgraph HA proxy binary that communicates with all nodes in the HA cluster
using the extended Bolt protocol and itself exposes a standard Bolt protocol to
the user. All standard Bolt clients (libraries and custom systems) can
communicate with the Memgraph HA proxy without any code modifications.
The HA proxy should be deployed on each client machine that is used to
communicate with the cluster. It can't be deployed on the Memgraph HA nodes!
When using the Memgraph HA proxy, the communication flow is described in the
following diagram:
```plaintext
Memgraph HA node 1 -----+
|
Memgraph HA node 2 -----+ Memgraph HA proxy <---> any standard Bolt client (C, Java, PHP, Python, etc.)
|
Memgraph HA node 3 -----+
```
To setup the Memgraph HA proxy the user should install the `memgraph_ha_proxy`
package.
After its successful installation, the user should enter all endpoints of the
HA Memgraph cluster servers into the configuration before attempting to start
the HA Memgraph proxy server.
The HA Memgraph proxy server loads all of its configuration from
`/etc/memgraph/memgraph_ha_proxy.conf`. Assuming that the cluster is set up
like in the previous examples, the user should uncomment and enter the following
value into the `--endpoints` parameter:
```plaintext
--endpoints=192.168.0.1:7687,192.168.0.2:7687,192.168.0.3:7687
```
Note that the IP addresses used in the example match the individual cluster
nodes IP addresses, but the ports used are the Bolt server ports exposed by
each node (currently the default value of `7687`).
The user can now start the proxy by using the following command:
```plaintext
systemctl start memgraph_ha_proxy
```
After the proxy has been started, the user can query the HA cluster by
connecting to the HA Memgraph proxy IP address using their favorite Bolt
client.
## Integration with Memgraph
The first thing that should be defined is a single instruction within the
context of Raft (i.e. a single entry in a replicated log). As mentioned
before, these instructions should be completely deterministic when applied
context of Raft (i.e. a single entry in a replicated log).
These instructions should be completely deterministic when applied
to the state machine. We have therefore decided that the appropriate level
of abstraction within Memgraph corresponds to `StateDelta`-s (data structures
of abstraction within Memgraph corresponds to `Delta`s (data structures
which describe a single change to the Memgraph state, used for durability
in WAL). Moreover, a single instruction in a replicated log will consist of a
batch of `StateDelta`s which correspond to a single **committed** transaction.
This decision both improves performance and handles some special cases that
present themselves otherwise by leveraging the knowledge that the transaction
should be committed.
batch of `Delta`s which correspond to a single transaction that's about
to be **committed**.
"What happens with aborted transactions?"
Apart from `Delta`s, there are certain operations within the storage called
`StorageGlobalOperations` which do not conform to usual transactional workflow
(e.g. Creating indices). Since our storage engine implementation guarantees
that at the moment of their execution no other transactions are active, we can
safely replicate them as well. In other words, no additional logic needs to be
implemented because of them.
A great question, they are handled solely by the leader which is the only
machine that communicates with the client. Aborted transactions do not alter
the state of the database and there is no need to replicate it to other machines
in the cluster. If, for instance, the leader dies before returning the result
of some read operation in an aborted transaction, the client will notice that
the leader has crashed. A new leader will be elected in the next term and the
client should retry the transaction.
Therefore, we will introduce a new `RaftDelta` object which can be constructed
both from storage `Delta` and `StorageGlobalOperation`. Instead of appending
these to WAL (as we do in single node), we will start to replicate them across
our cluster. Once we have replicated the corresponding Raft log entry on
majority of the cluster, we are able to safely commit the transaction or execute
a global operation. If for any reason the replication fails (leadership change,
worker failures, etc.) the transaction will be aborted.
"OK, that makes sense! But, wait a minute, this is broken by design! Merely
generating `StateDelta`s on the leader for any transaction will taint its
internal storage before sending the first RPC to some follower. This deviates
from Raft and will crash the universe!"
In the follower mode, we need to be able to apply `RaftDelta`s we got from
the leader when the protocol allows us to do so. In that case, we will use the
same concepts from durability in storage v2, i.e., applying deltas maps
completely to recovery from WAL in storage v2.
Another great observation. It is indeed true that applying `StateDelta`s makes
changes to local storage, but only a single type of `StateDelta` makes that
change durable. That `StateDelta` type is called `TRANSACTION_COMMIT` and we
will change its behaviour when working as a HA instance. More precisely, we
must not allow the transaction engine to modify the commit log saying that
the transaction has been committed. That action should be delayed until those
`StateDelta`s have been applied to the majority of the cluster. At that point
the commit log can be safely modified leaving it up to Raft to ensure the
durability of the transaction.
## Test and Benchmark Strategy
We should also address one subtle detail that arises in this case. Consider
the following scenario:
We have already implemented some integration and stress tests. These are:
* The leader starts working on a transaction which creates a new record in the
database. Suppose that record is stored in the leader's internal storage
but the transaction was not committed (i.e. no such entry in the commit log).
* The leader should start replicating those `StateDelta`s to its followers
but, suddenly, it's cut off from the rest of the cluster.
* Due to timeout, a new election is held and a new leader has been elected.
* Our old leader comes back to life and becomes a follower.
* The new leader receives a transaction which creates that same record, but
this transaction is successfully replicated and committed by the new leader.
1. leader election -- Tests whether leader election works properly.
2. basic test -- Tests basic leader election and log replication.
3. term updates test -- Tests a specific corner case (which used to fail)
regarding term updates.
4. log compaction test -- Tests whether log compaction works properly.
5. large log entries -- Tests whether we can successfully replicate relatively
large log entries.
6. index test -- Tests whether index creation works in HA.
7. normal operation stress test -- Long running concurrent stress test under
normal conditions (no failures).
8. read benchmark -- Measures read throughput in HA.
9. write benchmark -- Measures write throughput in HA.
The problem lies in the fact that there is still a record within the internal
storage of our old leader with the same transaction ID and GID as the recently
committed record by the new leader. Obviously, this is broken. As a solution, on
each transition from `Leader` to `Follower`, we will reinitialize storage, reset
the transaction engine and recover data from the Raft log. This will ensure all
ongoing transactions which have "polluted" the storage will be gone.
At the moment, our main goal is to pass existing tests and have a stable version
on our stress test. We should also implement a stress test which occasionally
introduces different types of failures in our cluster (we did this kind of
testing manually thus far). Passing these tests should convince us that we have
a "stable enough" version which we can start pushing to our customers.
"When will followers append that transaction to their commit logs?"
Additional (proper) testing should probably involve some ideas from
[here](https://jepsen.io/analyses/dgraph-1-0-2)
When the leader deduces that the transaction is safe to commit, it will include
the relevant information in all further heartbeats which will alert the
followers that it is safe to commit those entries from their raft logs.
Naturally, the followers need not to delay appending data to the commit log
as they know that the transaction has already been committed (from the clusters
point of view). If this sounds really messed up, seriously, read the Raft paper.
## Possible Future Changes/Improvements/Extensions
"How does the raft log differ from WAL"
There are two general directions in which we can alter HA Memgraph. The first
direction assumes we are going to stick with the Raft protocol. In that case
there are a few known ways to extend the basic algorithm in order to gain
better performance or achieve extra functionality. In no particular order,
these are:
Conceptually, it doesn't. When operating in HA, we don't really need the
recovery mechanisms implemented in Memgraph thus far. When a dead machine
comes back to life, it will eventually come in sync with the rest of the
cluster and everything will be done using the machine's raft log as well
as the messages received from the cluster leader.
1. Improving read performance using leader leases [Section 6.4 from Raft thesis]
2. Introducing cluster membership changes [Chapter 4 from Raft thesis]
3. Introducing a [learner mode](https://etcd.io/docs/v3.3.12/learning/learner/).
4. Consider different log compaction strategies [Chapter 5 from Raft thesis]
5. Removing HA proxy and implementing our own HA Memgraph client.
"Those logs will become huge, isn't that recovery going to be painfully slow?"
On the other hand, we might decide in the future to base our HA implementation
on a completely different protocol which might even offer different guarantees.
In that case we probably need to do a bit more of market research and weigh the
trade-offs of different solutions.
[This](https://www.postgresql.org/docs/9.5/different-replication-solutions.html)
might be a good starting point.
True, but there are mechanisms for making raft logs more compact. The most
popular method is, wait for it, making snapshots :)
Although the process of bringing an old machine back to life is a long one,
it doesn't really affect the performance of the cluster in a great degree.
The cluster will work perfectly fine with that machine being way out of sync.
## Reading materials
"I don't know, everything seems to be a lot slower than before!"
Absolutely true, the user should be aware that they will suffer dire
consequences on the performance side if they choose to be highly available.
As Frankie says, "That's life!".
"Also, I didn't really care about most of the things you've said. I'm
not a part of the storage team and couldn't care less about the issues you
face, how does HA affect 'my part of the codebase'?"
Answer for query execution: That's ok, you'll be able to use the same beloved
API (when we implement it, he he :) towards storage and continue to
make fun of us when you find a bug.
Answer for infrastructure: We'll talk. Some changes will surely need to
be made on the Memgraph client. There is a chapter in Diego's dissertation
called 'Client interaction', but we'll cross that bridge when we get there.
There will also be the whole 'integration with Jepsen tests' thing going on.
Answer for analytics: I'm astonished you've read this article. Wanna join
storage?
### Subtlety Regarding Reads
As we have hinted in the previous chapter, we would like to bypass log
replication for operations which do not alter the internal state of Memgraph.
Those operations should therefore be handled only by the leader, which is not
as trivial as it seems. The subtlety arises from the fact that a (newly-elected)
leader can have an entry in its log which was committed by the previous leader
that has crashed but that entry is not yet committed in its internal storage
by the current leader. Moreover, the rule about safely committing logs that are
replicated on the majority of the cluster only applies for entries replicated in
the leaders current term. Therefore, we are faced with two issues:
* We cannot simply perform read operations if the leader has a non-committed
entry in its log (breaks consistency).
* Replicating those entries onto the majority of the cluster is not enough
to guarantee that they can be safely committed.
This can be solved by introducing a blank no-op operation which the new leader
will try to replicate at the start of its term. Once that operation is
replicated and committed, the leader can safely perform those non-altering
operations on its own.
For further information about these issues, you should check out section
5.4.2 from the raft paper \[1\] which hints as to why its not safe to commit
entries from previous terms. Also, you should check out section 6.4 from
the thesis \[2\] which goes into more details around efficiently processing
read-only queries.
## How do we test HA
[Check this out](https://jepsen.io/analyses/dgraph-1-0-2)
1. [Raft paper](https://raft.github.io/raft.pdf)
2. [Raft thesis](https://github.com/ongardie/dissertation) (book.pdf)
3. [Raft playground](https://raft.github.io/)
4. [Leader Leases](https://blog.yugabyte.com/low-latency-reads-in-geo-distributed-sql-with-raft-leader-leases/)
5. [Improving Raft ETH](https://pub.tik.ee.ethz.ch/students/2017-FS/SA-2017-80.pdf)

View File

@@ -0,0 +1,185 @@
# Python 3 Query Modules
## Introduction
Memgraph exposes a C API for writing the so called Query Modules. These
modules contain definitions of procedures which can be invoked through the
query language using the `CALL ... YIELD ...` syntax. This mechanism allows
database users to extend Memgraph with their own algorithms and
functionalities.
Using a low level language like C can be quite cumbersome for writing modules,
so it seems natural to add support for a higher level language on top of the
existing C API.
There are languages written exactly for this purpose of extending C with high
level constructs, for example Lua and Guile. Instead of those, we have chosen
Python 3 to be the first high level language we will support. The primary reason
being that it's very popular, so more people should be able to write modules.
Another benefit of Python which comes out of its popularity is the large
ecosystem of libraries, especially graph algorithm related ones like NetworkX.
Python does have significant performance and implementation downsides compared
to Lua and Guile, but these are described in more detail later in this
document.
## Python 3 API Overview
The Python 3 API should be as user friendly as possible as well as look
Pythonic. This implies that some functions from the C API will not map to the
exact same functions. The most obvious case for a Pythonic approach is
registering procedures of a query module. Let's take a look at the C example
and its transformation to Python.
```c
static void procedure(const struct mgp_list *args,
const struct mgp_graph *graph, struct mgp_result *result,
struct mgp_memory *memory);
int mgp_init_module(struct mgp_module *module, struct mgp_memory *memory) {
struct mgp_proc *proc =
mgp_module_add_read_procedure(module, "procedure", procedure);
if (!proc) return 1;
if (!mgp_proc_add_arg(proc, "required_arg",
mgp_type_nullable(mgp_type_any())))
return 1;
struct mgp_value *null_value = mgp_value_make_null(memory);
if (!mgp_proc_add_opt_arg(proc, "optional_arg",
mgp_type_nullable(mgp_type_any()), null_value)) {
mgp_value_destroy(null_value);
return 1;
}
mgp_value_destroy(null_value);
if (!mgp_proc_add_result(proc, "result", mgp_type_string())) return 1;
if (!mgp_proc_add_result(proc, "args",
mgp_type_list(mgp_type_nullable(mgp_type_any()))))
return 1;
return 0;
}
```
In Python things should be a lot simpler.
```Python
# mgp.read_proc obtains the procedure name via __name__ attribute of a function.
@mgp.read_proc(# Arguments passed to multiple mgp_proc_add_arg calls
(('required_arg', mgp.Nullable(mgp.Any)), ('optional_arg', mgp.Nullable(mgp.Any), None)),
# Result fields passed to multiple mgp_proc_add_result calls
(('result', str), ('args', mgp.List(mgp.Nullable(mgp.Any)))))
def procedure(args, graph, result, memory):
pass
```
Here we have replaced `mgp_module_*` and `mgp_proc_*` C API with a much
simpler decorator function in Python -- `mgp.read_proc`. The types of
arguments and result fields can both be our types as well as Python builtin
types which can map to supported `mgp_value` types. The expected builtin types
we ought to support are: `bool`, `str`, `int`, `float` and `map`. While the
rest of the types are provided via our Python API. Optionally, we can add
convenience support for `object` type which would map to
`mgp.Nullable(mgp.Any)` and `list` which would map to
`mgp.List(mgp.Nullable(mgp.Any))`. Also, it makes sense to take a look if we
can leverage Python's `typing` module here.
Another Pythonic change is to remove `mgp_value` C API from Python altogether.
This means that the arguments a Python procedure receives are not `mgp_value`
instances but rather `PyObject` instances. In other words, our implementation
would immediately marshal `mgp_value` to corresponding type in Python.
Obviously we would need to provide our own Python types for non-builtin
things like `mgp.Vertex` (equivalent to `mgp_vertex`) and other.
Continuing from our example above, let's say the procedure was invoked through
Cypher using the following query.
MATCH (n) CALL py_module.procedure(42, n) YIELD *;
The Python procedure could then do the following and complete without throwing
neither the AssertionError nor the ValueError.
```Python
def procedure(args, graph, result, memory):
assert isinstance(args, list)
# Unpacking throws ValueError if args does not contain exactly 2 values.
required_arg, optional_arg = args
assert isintance(required_arg, int)
assert isinstance(optional_arg, mgp.Vertex)
```
The rest of the C API should naturally map to either top level functions or
class methods as appropriate.
## Loading Python Query Modules
Our current mechanism for loading the modules is to look for `.so` files in
the directory specified by `--query-modules` flag. This is done when Memgraph
is started. We can extend this mechanism to look for `.py` files in addition
to `.so` files in the same directory and import them in the embedded Python
interpreter. The only issue is embedding the interpreter in Memgraph. There
are multiple choices:
1. Building Memgraph and statically linking to Python.
2. Building Memgraph and dynamically linking to Python, and distributing
Python with Memgraph's installation.
3. Building Memgraph and dynamically linking to Python, but without
distributing the Python library.
4. Building Memgraph and optionally loading Python library by trying to
`dlopen` it.
The first two options are only viable if the Python license allows, and this
will need further investigation.
The third option adds Python as an installation dependency for Memgraph, and
without it Memgraph will not run. This is problematic for users which cannot
or do not want to install Python 3.
The fourth option avoids all of the issues present in the first 3 options, but
comes at a higher implementation cost. We would need to try to `dlopen` the
Python library and setup function pointers. If we succeed we would import
`.py` files from the `--query-modules` directory. On the other hand, if the
user does not have Python, `dlopen` would fail and Memgraph would run without
Python support.
After live discussion, we've decided to go with option 3. This way we don't
have to worry about mismatching Python versions we support and what the users
expect. Also, we should target Python 3.5 as that should be common between
Debian and CentOS for which we ship installation packages.
## Performance and Implementation Problems
As previously mentioned, embedding Python introduces usability issues compared
to other embeddable languages.
The first, major issue is Global Interpreter Lock (GIL). Initializing Python
will start a single global interpreter and running multiple threads will
require acquiring GIL. In practice, this means that when multiple users run a
procedure written in Python in parallel the execution will not actually be
parallel. Python's interpreter will jump between executing one user's
procedure and the other's. This can be quite an issue for long running
procedures when multiple users are querying Memgraph. The solution for this
issue is Python's API for sub-interpreters. Unfortunately, the support for
them is rather poor and the API contains a lot of critical bugs when we tried
to use them. For the time being, we will have to accept GIL and its downsides.
Perhaps in the future we will gain more knowledge on how we could reduce the
acquire rate of GIL or the sub-interpreter API will get improved.
Another major issue is memory allocation. Python's C API does not have support
for setting up a temporary allocator during execution of a single function.
It only has support for setting up a global heap allocator. This obviously
impacts our control of memory during a query procedure invocation. Besides
potential performance penalty, a procedure could allocate much more memory
than we would actually allow for execution of a single query. This means that
options controlling the memory limit during query execution are useless. On
the bright side, Python does use block style allocators and reference
counting, so the performance penalty and global memory usage should not be
that terrible.
The final issue that isn't as major as the ones above is the global state of
the interpreter. In practice this means that any registered procedure and
imported module has access to any other procedure and module. This may pollute
the namespace for other users, but it should not be much of a problem because
Python always has things under a module scope. The other, slightly bigger
downside is that a malicious user could use this knowledge to modify other
modules and procedures. This seems like a major issue, but if we take the
bigger picture into consideration, we already have a security issue in general
by invoking `dlopen` on `.so` and potentially running arbitrary code. This was
the trade off we chose to allow users to extend Memgraph. It's up to the users
to write sane extensions and protect their servers from access.

View File

@@ -0,0 +1,202 @@
# Replication
## High Level Context
Replication is a method that ensures that multiple database instances are
storing the same data. To enable replication, there must be at least two
instances of Memgraph in a cluster. Each instance has one of either two roles:
main or replica. The main instance is the instance that accepts writes to the
database and replicates its state to the replicas. In a cluster, there can only
be one main. There can be one or more replicas. None of the replicas will accept
write queries, but they will always accept read queries (there is an exception
to this rule and is described below). Replicas can also be configured to be
replicas of replicas, not necessarily replicas of the main. Each instance will
always be reachable using the standard supported communication protocols. The
replication will replicate WAL data. All data is transported through a custom
binary protocol that will try remain backward compatible, so that replication
immediately allows for zero downtime upgrades.
Each replica can be configured to accept replicated data in one of the following
modes:
- synchronous
- asynchronous
- semi-synchronous
### Synchronous Replication
When the data is replicated to a replica synchronously, all of the data of a
currently pending transaction must be sent to the synchronous replica before the
transaction is able to commit its changes.
This mode has a positive implication that all data that is committed to the
main will always be replicated to the synchronous replica. It also has a
negative performance implication because non-responsive replicas could grind all
query execution to a halt.
This mode is good when you absolutely need to be sure that all data is always
consistent between the main and the replica.
### Asynchronous Replication
When the data is replicated to a replica asynchronously, all pending
transactions are immediately committed and their data is replicated to the
asynchronous replica in the background.
This mode has a positive performance implication in which it won't slow down
query execution. It also has a negative implication that the data between the
main and the replica is almost never in a consistent state (when the data is
being changed).
This mode is good when you don't care about consistency and only need an
eventually consistent cluster, but you care about performance.
### Semi-synchronous Replication
When the data is replicated to a replica semi-synchronously, the data is
replicated using both the synchronous and asynchronous methodology. The data is
always replicated synchronously, but, if the replica for any reason doesn't
respond within a preset timeout, the pending transaction is committed and the
data is replicated to the replica asynchronously.
This mode has a positive implication that all data that is committed is
*mostly* replicated to the semi-synchronous replica. It also has a negative
performance implication as the synchronous replication mode.
This mode is useful when you want the replication to be synchronous to ensure
that the data within the cluster is consistent, but you don't want the main
to grind to a halt when you have a non-responsive replica.
### Addition of a New Replica
Each replica, when added to the cluster (in any mode), will first start out as
an asynchronous replica. That will allow replicas that have fallen behind to
first catch-up to the current state of the database. When the replica is in a
state that it isn't lagging behind the main it will then be promoted (in a brief
stop-the-world operation) to a semi-synchronous or synchronous replica. Slaves
that are added as asynchronous replicas will remain asynchronous.
## User Facing Setup
### How to Setup a Memgraph Cluster with Replication?
Replication configuration is done primarily through openCypher commands. This
allows the cluster to be dynamically rearranged (new leader election, addition
of a new replica, etc.).
Each Memgraph instance when first started will be a main. You have to change
the role of all replica nodes using the following openCypher query before you
can enable replication on the main:
```plaintext
SET REPLICATION ROLE TO (MAIN|REPLICA) WITH PORT <port_number>;
```
Note that the "WITH PORT <port_number>" part of the query sets the replication port,
but it applies only to the replica. In other words, if you try to set the
replication port as the main, a semantic exception will be thrown.
After you have set your replica instance to the correct operating role, you can
enable replication in the main instance by issuing the following openCypher
command:
```plaintext
REGISTER REPLICA name (SYNC|ASYNC) [WITH TIMEOUT 0.5] TO <socket_address>;
```
The socket address must be a string of the following form:
```plaintext
"IP_ADDRESS:PORT_NUMBER"
```
where IP_ADDRESS is a valid IP address, and PORT_NUMBER is a valid port number,
both given in decimal notation.
Note that in this case they must be separated by a single colon.
Alternatively, one can give the socket address as:
```plaintext
"IP_ADDRESS"
```
where IP_ADDRESS must be a valid IP address, and the port number will be
assumed to be the default one (we specify it to be 10000).
Each Memgraph instance will remember what the configuration was set to and will
automatically resume with its role when restarted.
### How to Setup an Advanced Replication Scenario?
The configuration allows for a more advanced scenario like this:
```plaintext
main -[asynchronous]-> replica 1 -[semi-synchronous]-> replica 2
```
To configure the above scenario, issue the following commands:
```plaintext
SET REPLICATION ROLE TO REPLICA; # on replica 1
SET REPLICATION ROLE TO REPLICA; # on replica 2
REGISTER REPLICA replica1 ASYNC TO <replica1_sa>; # on main
REGISTER REPLICA replica2 SYNC WITH TIMEOUT 0.5 TO <replica2_sa>; # on replica 1
```
### How to See the Current Replication Status?
To see the replication ROLE of the current Memgraph instance, you can issue the
following query:
```plaintext
SHOW REPLICATION ROLE;
```
To see the replicas of the current Memgraph instance, you can issue the
following query:
```plaintext
SHOW REPLICAS;
```
To delete a replica, issue the following query:
```plaintext
DELETE REPLICA 'name';
```
### How to Promote a New Main?
When you have an already set-up cluster, to promote a new main, just set the
replica that you want to be a main to the main role.
```plaintext
SET REPLICATION ROLE TO MAIN; # on desired replica
```
After the command is issued, if the original main is still alive, it won't be
able to replicate its data to the replica (the new main) anymore and will enter
an error state. You must ensure that at any given point in time there aren't
two mains in the cluster.
## Integration with Memgraph
WAL `Delta`s are replicated between the replication main and replica. With
`Delta`s, all `StorageGlobalOperation`s are also replicated. Replication is
essentially the same as appending to the WAL.
Synchronous replication will occur in `Commit` and each
`StorageGlobalOperation` handler. The storage itself guarantees that `Commit`
will be called single-threadedly and that no `StorageGlobalOperation` will be
executed during an active transaction. Asynchronous replication will load its
data from already written WAL files and transmit the data to the replica. All
data will be replicated using our RPC protocol (SLK encoded).
For each replica the replication main (or replica) will keep track of the
replica's state. That way, it will know which operations must be transmitted to
the replica and which operations can be skipped. When a replica is very stale,
a snapshot will be transmitted to it so that it can quickly synchronize with
the current state. All following operations will transmit WAL deltas.
## Reading materials
1. [PostgreSQL comparison of different solutions](https://www.postgresql.org/docs/12/different-replication-solutions.html)
2. [PostgreSQL docs](https://www.postgresql.org/docs/12/runtime-config-replication.html)
3. [MySQL reference manual](https://dev.mysql.com/doc/refman/8.0/en/replication.html)
4. [MySQL docs](https://dev.mysql.com/doc/refman/8.0/en/replication-setup-slaves.html)
5. [MySQL master switch](https://dev.mysql.com/doc/refman/8.0/en/replication-solutions-switch.html)

44
environment/README.md Normal file
View File

@@ -0,0 +1,44 @@
# Memgraph Build and Run Environments
## Toolchain Installation Procedure
1) Download the toolchain for your operating system from one of the following
links (current active toolchain is `toolchain-v2`):
* [CentOS 7](https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/toolchain-v2/toolchain-v2-binaries-centos-7.tar.gz)
* [CentOS 8](https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/toolchain-v2/toolchain-v2-binaries-centos-8.tar.gz)
* [Debian 9](https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/toolchain-v2/toolchain-v2-binaries-debian-9.tar.gz)
* [Debian 10](https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/toolchain-v2/toolchain-v2-binaries-debian-10.tar.gz)
* [Ubuntu 18.04](https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/toolchain-v2/toolchain-v2-binaries-ubuntu-18.04.tar.gz)
* [Ubuntu 20.04](https://s3-eu-west-1.amazonaws.com/deps.memgraph.io/toolchain-v2/toolchain-v2-binaries-ubuntu-20.04.tar.gz)
2) Extract the toolchain with the following command:
```bash
tar xzvf {{toolchain-archive}}.tar.gz -C /opt
```
3) Check and install required toolchain runtime dependencies by executing
(e.g., on **Debian 10**):
```bash
./environment/os/debian-10.sh check TOOLCHAIN_RUN_DEPS
./environment/os/debian-10.sh install TOOLCHAIN_RUN_DEPS
```
4) Activate the toolchain:
```bash
source /opt/toolchain-v2/activate
```
## Toolchain Upgrade Procedure
1) Build a new toolchain for each supported OS (latest versions).
2) If the new toolchain doesn't compile on some supported OS, the last
compilable toolchain has to be used instead. In other words, the project has
to compile on the oldest active toolchain as well. Suppose some
changes/improvements were added when migrating to the latest toolchain; in
that case, the maintainer has to ensure that the project still compiles on
previous toolchains (everything from `init` script to the actual code has to
work on all supported operating systems).

3
environment/os/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*.deb
*.rpm
*.tar.gz

127
environment/os/centos-7.sh Executable file
View File

@@ -0,0 +1,127 @@
#!/bin/bash
set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh"
TOOLCHAIN_BUILD_DEPS=(
coreutils gcc gcc-c++ make # generic build tools
wget # used for archive download
gnupg2 # used for archive signature verification
tar gzip bzip2 xz unzip # used for archive unpacking
zlib-devel # zlib library used for all builds
expat-devel libipt-devel libbabeltrace-devel xz-devel python3-devel # gdb
texinfo # gdb
libcurl-devel # cmake
readline-devel # cmake and llvm
libffi-devel libxml2-devel perl-Digest-MD5 # llvm
libedit-devel pcre-devel automake bison # swig
)
TOOLCHAIN_RUN_DEPS=(
make # generic build tools
tar gzip bzip2 xz # used for archive unpacking
zlib # zlib library used for all builds
expat libipt libbabeltrace xz-libs python3 # for gdb
readline # for cmake and llvm
libffi libxml2 # for llvm
)
MEMGRAPH_BUILD_DEPS=(
git # source code control
make pkgconfig # build system
curl wget # for downloading libs
libuuid-devel java-11-openjdk # required by antlr
readline-devel # for memgraph console
python3-devel # for query modules
openssl-devel
libseccomp-devel
python3 python-virtualenv python3-pip nmap-ncat # for qa, macro_benchmark and stress tests
#
# IMPORTANT: python3-yaml does NOT exist on CentOS
# Install it using `pip3 install PyYAML`
#
PyYAML # Package name here does not correspond to the yum package!
libcurl-devel # mg-requests
sbcl # for custom Lisp C++ preprocessing
rpm-build rpmlint # for RPM package building
doxygen graphviz # source documentation generators
which mono-complete dotnet-sdk-3.1 golang nodejs zip unzip java-11-openjdk-devel # for driver tests
)
list() {
echo "$1"
}
check() {
local missing=""
for pkg in $1; do
if [ "$pkg" == git ]; then
if ! which "git" >/dev/null; then
missing="git $missing"
fi
continue
fi
if [ "$pkg" == "PyYAML" ]; then
if ! python3 -c "import yaml" >/dev/null 2>/dev/null; then
missing="$pkg $missing"
fi
continue
fi
if ! yum list installed "$pkg" >/dev/null 2>/dev/null; then
missing="$pkg $missing"
fi
done
if [ "$missing" != "" ]; then
echo "MISSING PACKAGES: $missing"
exit 1
fi
}
install() {
cd "$DIR"
if [ "$EUID" -ne 0 ]; then
echo "Please run as root."
exit 1
fi
if [ "$SUDO_USER" == "" ]; then
echo "Please run as sudo."
exit 1
fi
# If GitHub Actions runner is installed, append LANG to the environment.
# Python related tests doesn't work the LANG export.
if [ -d "/home/gh/actions-runner" ]; then
echo "LANG=en_US.utf8" >> /home/gh/actions-runner/.env
else
echo "NOTE: export LANG=en_US.utf8"
fi
yum install -y epel-release
yum update -y
yum install -y wget git python3 python3-pip
for pkg in $1; do
if [ "$pkg" == libipt ]; then
if ! yum list installed libipt >/dev/null 2>/dev/null; then
yum install -y http://repo.okay.com.mx/centos/8/x86_64/release/libipt-1.6.1-8.el8.x86_64.rpm
fi
continue
fi
if [ "$pkg" == libipt-devel ]; then
if ! yum list installed libipt-devel >/dev/null 2>/dev/null; then
yum install -y http://repo.okay.com.mx/centos/8/x86_64/release/libipt-devel-1.6.1-8.el8.x86_64.rpm
fi
continue
fi
if [ "$pkg" == dotnet-sdk-3.1 ]; then
if ! yum list installed dotnet-sdk-3.1 >/dev/null 2>/dev/null; then
wget -nv https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm -O packages-microsoft-prod.rpm
rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm
yum update -y
yum install -y dotnet-sdk-3.1
fi
continue
fi
if [ "$pkg" == PyYAML ]; then
sudo -H -u "$SUDO_USER" bash -c "pip3 install --user PyYAML"
continue
fi
yum install -y "$pkg"
done
}
deps=$2"[*]"
"$1" "${!deps}"

144
environment/os/centos-8.sh Executable file
View File

@@ -0,0 +1,144 @@
#!/bin/bash
set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh"
TOOLCHAIN_BUILD_DEPS=(
coreutils gcc gcc-c++ make # generic build tools
wget # used for archive download
gnupg2 # used for archive signature verification
tar gzip bzip2 xz unzip # used for archive unpacking
zlib-devel # zlib library used for all builds
expat-devel libipt-devel libbabeltrace-devel xz-devel python36-devel texinfo # for gdb
libcurl-devel # for cmake
readline-devel # for cmake and llvm
libffi-devel libxml2-devel # for llvm
libedit-devel pcre-devel automake bison # for swig
)
TOOLCHAIN_RUN_DEPS=(
make # generic build tools
tar gzip bzip2 xz # used for archive unpacking
zlib # zlib library used for all builds
expat libipt libbabeltrace xz-libs python36 # for gdb
readline # for cmake and llvm
libffi libxml2 # for llvm
)
MEMGRAPH_BUILD_DEPS=(
git # source code control
make pkgconf-pkg-config # build system
curl wget # for downloading libs
libuuid-devel java-11-openjdk # required by antlr
readline-devel # for memgraph console
python36-devel # for query modules
openssl-devel
libseccomp-devel
python36 python3-virtualenv python3-pip nmap-ncat # for qa, macro_benchmark and stress tests
#
# IMPORTANT: python3-yaml does NOT exist on CentOS
# Install it manually using `pip3 install PyYAML`
#
PyYAML # Package name here does not correspond to the yum package!
libcurl-devel # mg-requests
rpm-build rpmlint # for RPM package building
doxygen graphviz # source documentation generators
which mono-complete dotnet-sdk-3.1 nodejs golang zip unzip java-11-openjdk-devel # for driver tests
sbcl # for custom Lisp C++ preprocessing
)
list() {
echo "$1"
}
check() {
local missing=""
for pkg in $1; do
if [ "$pkg" == "PyYAML" ]; then
if ! python3 -c "import yaml" >/dev/null 2>/dev/null; then
missing="$pkg $missing"
fi
continue
fi
if ! yum list installed "$pkg" >/dev/null 2>/dev/null; then
missing="$pkg $missing"
fi
done
if [ "$missing" != "" ]; then
echo "MISSING PACKAGES: $missing"
exit 1
fi
}
install() {
cd "$DIR"
if [ "$EUID" -ne 0 ]; then
echo "Please run as root."
exit 1
fi
if [ "$SUDO_USER" == "" ]; then
echo "Please run as sudo."
exit 1
fi
# If GitHub Actions runner is installed, append LANG to the environment.
# Python related tests doesn't work the LANG export.
if [ -d "/home/gh/actions-runner" ]; then
echo "LANG=en_US.utf8" >> /home/gh/actions-runner/.env
else
echo "NOTE: export LANG=en_US.utf8"
fi
dnf install -y epel-release
dnf config-manager --set-enabled PowerTools # Required to install texinfo.
dnf update -y
dnf install -y wget git python36 python3-pip
for pkg in $1; do
if [ "$pkg" == libipt ]; then
if ! dnf list installed libipt >/dev/null 2>/dev/null; then
dnf install -y http://repo.okay.com.mx/centos/8/x86_64/release/libipt-1.6.1-8.el8.x86_64.rpm
fi
continue
fi
if [ "$pkg" == libipt-devel ]; then
if ! yum list installed libipt-devel >/dev/null 2>/dev/null; then
dnf install -y http://repo.okay.com.mx/centos/8/x86_64/release/libipt-devel-1.6.1-8.el8.x86_64.rpm
fi
continue
fi
# Install GDB dependencies not present in the standard repos.
# https://bugs.centos.org/view.php?id=17068
# https://centos.pkgs.org
# Since 2020, there is Babeltrace2 (https://babeltrace.org). Not used
# within GDB yet (an assumption).
if [ "$pkg" == libbabeltrace-devel ]; then
if ! dnf list installed libbabeltrace-devel >/dev/null 2>/dev/null; then
dnf install -y http://repo.okay.com.mx/centos/8/x86_64/release/libbabeltrace-devel-1.5.4-2.el8.x86_64.rpm
fi
continue
fi
if [ "$pkg" == sbcl ]; then
if ! dnf list installed cl-asdf >/dev/null 2>/dev/null; then
dnf install -y https://pkgs.dyn.su/el8/base/x86_64/cl-asdf-20101028-18.el8.noarch.rpm
fi
if ! dnf list installed common-lisp-controller >/dev/null 2>/dev/null; then
dnf install -y https://pkgs.dyn.su/el8/base/x86_64/common-lisp-controller-7.4-20.el8.noarch.rpm
fi
if ! dnf list installed sbcl >/dev/null 2>/dev/null; then
dnf install -y https://pkgs.dyn.su/el8/base/x86_64/sbcl-2.0.1-4.el8.x86_64.rpm
fi
continue
fi
if [ "$pkg" == dotnet-sdk-3.1 ]; then
if ! dnf list installed dotnet-sdk-3.1 >/dev/null 2>/dev/null; then
wget -nv https://packages.microsoft.com/config/centos/8/packages-microsoft-prod.rpm -O packages-microsoft-prod.rpm
rpm -Uvh https://packages.microsoft.com/config/centos/8/packages-microsoft-prod.rpm
dnf update -y
dnf install -y dotnet-sdk-3.1
fi
continue
fi
if [ "$pkg" == PyYAML ]; then
sudo -H -u "$SUDO_USER" bash -c "pip3 install --user PyYAML"
continue
fi
dnf install -y "$pkg"
done
}
deps=$2"[*]"
"$1" "${!deps}"

86
environment/os/debian-10.sh Executable file
View File

@@ -0,0 +1,86 @@
#!/bin/bash
set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh"
TOOLCHAIN_BUILD_DEPS=(
coreutils gcc g++ build-essential make # generic build tools
wget # used for archive download
gnupg # used for archive signature verification
tar gzip bzip2 xz-utils unzip # used for archive unpacking
zlib1g-dev # zlib library used for all builds
libexpat1-dev libipt-dev libbabeltrace-dev liblzma-dev python3-dev texinfo # for gdb
libcurl4-openssl-dev # for cmake
libreadline-dev # for cmake and llvm
libffi-dev libxml2-dev # for llvm
libedit-dev libpcre3-dev automake bison # for swig
)
TOOLCHAIN_RUN_DEPS=(
make # generic build tools
tar gzip bzip2 xz-utils # used for archive unpacking
zlib1g # zlib library used for all builds
libexpat1 libipt2 libbabeltrace1 liblzma5 python3 # for gdb
libcurl4 # for cmake
libreadline7 # for cmake and llvm
libffi6 libxml2 # for llvm
)
MEMGRAPH_BUILD_DEPS=(
git # source code control
make pkg-config # build system
curl wget # for downloading libs
uuid-dev default-jre-headless # required by antlr
libreadline-dev # for memgraph console
libpython3-dev python3-dev # for query modules
libssl-dev
libseccomp-dev
netcat # tests are using nc to wait for memgraph
python3 virtualenv python3-virtualenv python3-pip # for qa, macro_benchmark and stress tests
python3-yaml # for the configuration generator
libcurl4-openssl-dev # mg-requests
sbcl # for custom Lisp C++ preprocessing
doxygen graphviz # source documentation generators
mono-runtime mono-mcs zip unzip default-jdk-headless # for driver tests
dotnet-sdk-3.1 golang nodejs npm
)
list() {
echo "$1"
}
check() {
check_all_dpkg "$1"
}
install() {
cat >/etc/apt/sources.list <<EOF
deb http://deb.debian.org/debian/ buster main non-free contrib
deb-src http://deb.debian.org/debian/ buster main non-free contrib
deb http://deb.debian.org/debian/ buster-updates main contrib non-free
deb-src http://deb.debian.org/debian/ buster-updates main contrib non-free
deb http://security.debian.org/debian-security buster/updates main contrib non-free
deb-src http://security.debian.org/debian-security buster/updates main contrib non-free
EOF
cd "$DIR"
apt update
# If GitHub Actions runner is installed, append LANG to the environment.
# Python related tests doesn't work the LANG export.
if [ -d "/home/gh/actions-runner" ]; then
echo "LANG=en_US.utf8" >> /home/gh/actions-runner/.env
else
echo "NOTE: export LANG=en_US.utf8"
fi
apt install -y wget
for pkg in $1; do
if [ "$pkg" == dotnet-sdk-3.1 ]; then
if ! dpkg -s "$pkg" 2>/dev/null >/dev/null; then
wget -nv https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
dpkg -i packages-microsoft-prod.deb
apt-get update
apt-get install -y apt-transport-https dotnet-sdk-3.1
fi
continue
fi
apt install -y "$pkg"
done
}
deps=$2"[*]"
"$1" "${!deps}"

55
environment/os/debian-9.sh Executable file
View File

@@ -0,0 +1,55 @@
#!/bin/bash
set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh"
TOOLCHAIN_BUILD_DEPS=(
coreutils gcc g++ build-essential make # generic build tools
wget # used for archive download
gnupg # used for archive signature verification
tar gzip bzip2 xz-utils unzip # used for archive unpacking
zlib1g-dev # zlib library used for all builds
libexpat1-dev libipt-dev libbabeltrace-dev libbabeltrace-ctf-dev liblzma-dev python3-dev texinfo # for gdb
libcurl4-openssl-dev # for cmake
libreadline-dev # for cmake and llvm
libffi-dev libxml2-dev # for llvm
libedit-dev libpcre3-dev automake bison # for swig
)
TOOLCHAIN_RUN_DEPS=(
make # generic build tools
tar gzip bzip2 xz-utils # used for archive unpacking
zlib1g # zlib library used for all builds
libexpat1 libipt1 libbabeltrace1 libbabeltrace-ctf1 liblzma5 python3 # for gdb
libcurl3 # for cmake
libreadline7 # for cmake and llvm
libffi6 libxml2 # for llvm
)
MEMGRAPH_BUILD_DEPS=(
git # source code control
make pkg-config # build system
curl wget # for downloading libs
uuid-dev default-jre-headless # required by antlr
libreadline-dev # for memgraph console
libpython3-dev python3-dev # for query modules
libssl-dev
libseccomp-dev
python3 python-virtualenv python3-pip # for qa, macro_benchmark and stress tests
python3-yaml # for the configuration generator
libcurl4-openssl-dev # mg-requests
sbcl # for custom Lisp C++ preprocessing
doxygen graphviz # source documentation generators
mono-runtime mono-mcs nodejs zip unzip default-jdk-headless # for driver tests
)
list() {
echo "$1"
}
check() {
check_all_dpkg "$1"
}
install() {
install_all_apt "$1"
}
deps=$2"[*]"
"$1" "${!deps}"

30
environment/os/template.sh Executable file
View File

@@ -0,0 +1,30 @@
#!/bin/bash
set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh"
TOOLCHAIN_BUILD_DEPS=(
pkg
)
TOOLCHAIN_RUN_DEPS=(
pkg
)
MEMGRAPH_BUILD_DEPS=(
pkg
)
list() {
echo "$1"
}
check() {
echo "TODO: Implement ${FUNCNAME[0]}."
exit 1
}
install() {
echo "TODO: Implement ${FUNCNAME[0]}."
exit 1
}
# http://ahmed.amayem.com/bash-indirect-expansion-exploration
deps=$2"[*]"
"$1" "${!deps}"

56
environment/os/ubuntu-18.04.sh Executable file
View File

@@ -0,0 +1,56 @@
#!/bin/bash
set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh"
TOOLCHAIN_BUILD_DEPS=(
coreutils gcc g++ build-essential make # generic build tools
wget # archive download
gnupg # archive signature verification
tar gzip bzip2 xz-utils unzip # archive unpacking
zlib1g-dev # zlib library used for all builds
libexpat1-dev libipt-dev libbabeltrace-dev liblzma-dev python3-dev # gdb
texinfo # gdb
libcurl4-openssl-dev # cmake
libreadline-dev # cmake and llvm
libffi-dev libxml2-dev # llvm
libedit-dev libpcre3-dev automake bison # swig
)
TOOLCHAIN_RUN_DEPS=(
make # generic build tools
tar gzip bzip2 xz-utils # used for archive unpacking
zlib1g # zlib library used for all builds
libexpat1 libipt1 libbabeltrace1 liblzma5 python3 # for gdb
libcurl4 # for cmake
libreadline7 # for cmake and llvm
libffi6 libxml2 # for llvm
)
MEMGRAPH_BUILD_DEPS=(
git # source code control
make pkg-config # build system
curl wget # downloading libs
uuid-dev default-jre-headless # required by antlr
libreadline-dev # memgraph console
libpython3-dev python3-dev # for query modules
libssl-dev
libseccomp-dev
python3 virtualenv python3-virtualenv python3-pip # qa, macro bench and stress tests
python3-yaml # the configuration generator
libcurl4-openssl-dev # mg-requests
sbcl # custom Lisp C++ preprocessing
doxygen graphviz # source documentation generators
mono-runtime mono-mcs nodejs zip unzip default-jdk-headless # driver tests
)
list() {
echo "$1"
}
check() {
check_all_dpkg "$1"
}
install() {
apt install -y $1
}
deps=$2"[*]"
"$1" "${!deps}"

78
environment/os/ubuntu-20.04.sh Executable file
View File

@@ -0,0 +1,78 @@
#!/bin/bash
set -Eeuo pipefail
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source "$DIR/../util.sh"
TOOLCHAIN_BUILD_DEPS=(
coreutils gcc g++ build-essential make # generic build tools
wget # used for archive download
gnupg # used for archive signature verification
tar gzip bzip2 xz-utils unzip # used for archive unpacking
zlib1g-dev # zlib library used for all builds
libexpat1-dev libipt-dev libbabeltrace-dev liblzma-dev python3-dev texinfo # for gdb
libcurl4-openssl-dev # for cmake
libreadline-dev # for cmake and llvm
libffi-dev libxml2-dev # for llvm
libedit-dev libpcre3-dev automake bison # for swig
)
TOOLCHAIN_RUN_DEPS=(
make # generic build tools
tar gzip bzip2 xz-utils # used for archive unpacking
zlib1g # zlib library used for all builds
libexpat1 libipt2 libbabeltrace1 liblzma5 python3 # for gdb
libcurl4 # for cmake
libreadline8 # for cmake and llvm
libffi7 libxml2 # for llvm
)
MEMGRAPH_BUILD_DEPS=(
git # source code control
make pkg-config # build system
curl wget # for downloading libs
uuid-dev default-jre-headless # required by antlr
libreadline-dev # for memgraph console
libpython3-dev python3-dev # for query modules
libssl-dev
libseccomp-dev
netcat # tests are using nc to wait for memgraph
python3 python3-virtualenv python3-pip # for qa, macro_benchmark and stress tests
python3-yaml # for the configuration generator
libcurl4-openssl-dev # mg-requests
sbcl # for custom Lisp C++ preprocessing
doxygen graphviz # source documentation generators
mono-runtime mono-mcs zip unzip default-jdk-headless # for driver tests
dotnet-sdk-3.1 golang nodejs npm
)
list() {
echo "$1"
}
check() {
check_all_dpkg "$1"
}
install() {
cd "$DIR"
apt update
# If GitHub Actions runner is installed, append LANG to the environment.
# Python related tests doesn't work the LANG export.
if [ -d "/home/gh/actions-runner" ]; then
echo "LANG=en_US.utf8" >> /home/gh/actions-runner/.env
else
echo "NOTE: export LANG=en_US.utf8"
fi
apt install -y wget
for pkg in $1; do
if [ "$pkg" == dotnet-sdk-3.1 ]; then
if ! dpkg -s dotnet-sdk-3.1 2>/dev/null >/dev/null; then
wget -nv https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
dpkg -i packages-microsoft-prod.deb
apt-get update
apt-get install -y apt-transport-https dotnet-sdk-3.1
fi
continue
fi
apt install -y "$pkg"
done
}
deps=$2"[*]"
"$1" "${!deps}"

651
environment/toolchain/v1.sh Executable file
View File

@@ -0,0 +1,651 @@
#!/bin/bash -e
# helpers
pushd () { command pushd "$@" > /dev/null; }
popd () { command popd "$@" > /dev/null; }
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
CPUS=$( cat /proc/cpuinfo | grep processor | wc -l )
cd "$DIR"
# toolchain version
TOOLCHAIN_VERSION=1
# package versions used
GCC_VERSION=8.3.0
BINUTILS_VERSION=2.32
GDB_VERSION=8.2.1
CMAKE_VERSION=3.14.2
CPPCHECK_VERSION=1.87
LLVM_VERSION=8.0.0
SWIG_VERSION=3.0.12 # used only for LLVM compilation
# check for installed dependencies
DISTRO="$( egrep '^(VERSION_)?ID=' /etc/os-release | sort | cut -d '=' -f 2- | sed 's/"//g' | paste -s -d '-' )"
case "$DISTRO" in
debian-9)
DEPS_MANAGER=apt-get
DEPS_COMPILE=(
coreutils gcc g++ build-essential make # generic build tools
wget # used for archive download
gnupg # used for archive signature verification
tar gzip bzip2 xz-utils unzip # used for archive unpacking
zlib1g-dev # zlib library used for all builds
libexpat1-dev libipt-dev libbabeltrace-dev libbabeltrace-ctf-dev liblzma-dev python3-dev texinfo # for gdb
libcurl4-openssl-dev # for cmake
libreadline-dev # for cmake and llvm
libffi-dev libxml2-dev # for llvm
libedit-dev libpcre3-dev automake bison # for swig
)
DEPS_RUN=(
make # generic build tools
tar gzip bzip2 xz-utils # used for archive unpacking
zlib1g # zlib library used for all builds
libexpat1 libipt1 libbabeltrace1 libbabeltrace-ctf1 liblzma5 python3 # for gdb
libcurl3 # for cmake
libreadline7 # for cmake and llvm
libffi6 libxml2 # for llvm
)
;;
debian-10)
DEPS_MANAGER=apt-get
DEPS_COMPILE=(
coreutils gcc g++ build-essential make # generic build tools
wget # used for archive download
gnupg # used for archive signature verification
tar gzip bzip2 xz-utils unzip # used for archive unpacking
zlib1g-dev # zlib library used for all builds
libexpat1-dev libipt-dev libbabeltrace-dev liblzma-dev python3-dev texinfo # for gdb
libcurl4-openssl-dev # for cmake
libreadline-dev # for cmake and llvm
libffi-dev libxml2-dev # for llvm
libedit-dev libpcre3-dev automake bison # for swig
)
DEPS_RUN=(
make # generic build tools
tar gzip bzip2 xz-utils # used for archive unpacking
zlib1g # zlib library used for all builds
libexpat1 libipt2 libbabeltrace1 liblzma5 python3 # for gdb
libcurl4 # for cmake
libreadline7 # for cmake and llvm
libffi6 libxml2 # for llvm
)
;;
ubuntu-18.04)
DEPS_MANAGER=apt-get
DEPS_COMPILE=(
coreutils gcc g++ build-essential make # generic build tools
wget # used for archive download
gnupg # used for archive signature verification
tar gzip bzip2 xz-utils unzip # used for archive unpacking
zlib1g-dev # zlib library used for all builds
libexpat1-dev libipt-dev libbabeltrace-dev liblzma-dev python3-dev texinfo # for gdb
libcurl4-openssl-dev # for cmake
libreadline-dev # for cmake and llvm
libffi-dev libxml2-dev # for llvm
libedit-dev libpcre3-dev automake bison # for swig
)
DEPS_RUN=(
make # generic build tools
tar gzip bzip2 xz-utils # used for archive unpacking
zlib1g # zlib library used for all builds
libexpat1 libipt1 libbabeltrace1 liblzma5 python3 # for gdb
libcurl4 # for cmake
libreadline7 # for cmake and llvm
libffi6 libxml2 # for llvm
)
;;
centos-7)
DEPS_MANAGER=yum
DEPS_COMPILE=(
coreutils gcc gcc-c++ make # generic build tools
wget # used for archive download
gnupg2 # used for archive signature verification
tar gzip bzip2 xz unzip # used for archive unpacking
zlib-devel # zlib library used for all builds
expat-devel libipt-devel libbabeltrace-devel xz-devel python3-devel texinfo # for gdb
libcurl-devel # for cmake
readline-devel # for cmake and llvm
libffi-devel libxml2-devel # for llvm
libedit-devel pcre-devel automake bison # for swig
)
DEPS_RUN=(
make # generic build tools
tar gzip bzip2 xz # used for archive unpacking
zlib # zlib library used for all builds
expat libipt libbabeltrace xz-libs python3 # for gdb
readline # for cmake and llvm
libffi libxml2 # for llvm
)
;;
*)
echo "Unknown distribution: $DISTRO!"
exit 1
;;
esac
find_missing_dependencies () {
local message="$1"; shift
local missing=""
while [ "$1" != "" ]; do
if [ "$DEPS_MANAGER" == "apt-get" ]; then
if ! dpkg -s $1 >/dev/null 2>/dev/null; then
missing="$1 $missing"
fi
elif [ "$DEPS_MANAGER" == "yum" ]; then
if ! yum list installed $1 >/dev/null 2>/dev/null; then
missing="$1 $missing"
fi
else
echo "Invalid package manager: $DEPS_MANAGER!"
exit 1
fi
shift
done
if [ "$missing" != "" ]; then
echo "$message: $missing"
exit 1
fi
}
find_missing_dependencies "Missing dependencies" ${DEPS_COMPILE[@]}
find_missing_dependencies "All dependencies are installed, but the following runtime libraries were not found (they are probably invalid)" ${DEPS_RUN[@]}
# check installation directory
NAME=toolchain-v$TOOLCHAIN_VERSION
PREFIX=/opt/$NAME
mkdir -p $PREFIX >/dev/null 2>/dev/null || true
if [ ! -d $PREFIX ] || [ ! -w $PREFIX ]; then
echo "Please make sure that the directory '$PREFIX' exists and is writable by the current user!"
echo
echo "If unsure, execute these commands as root:"
echo " mkdir $PREFIX && chown $USER:$USER $PREFIX"
echo
echo "Press <return> when you have created the directory and granted permissions."
# wait for the directory to be created
while true; do
read
if [ ! -d $PREFIX ] || [ ! -w $PREFIX ]; then
echo
echo "You can't continue before you have created the directory and granted permissions!"
echo
echo "Press <return> when you have created the directory and granted permissions."
else
break
fi
done
fi
# create archives directory
mkdir -p archives
# download all archives
pushd archives
if [ ! -f gcc-$GCC_VERSION.tar.gz ]; then
wget https://ftp.gnu.org/gnu/gcc/gcc-$GCC_VERSION/gcc-$GCC_VERSION.tar.gz
fi
if [ ! -f binutils-$BINUTILS_VERSION.tar.gz ]; then
wget https://ftp.gnu.org/gnu/binutils/binutils-$BINUTILS_VERSION.tar.gz
fi
if [ ! -f gdb-$GDB_VERSION.tar.gz ]; then
wget https://ftp.gnu.org/gnu/gdb/gdb-$GDB_VERSION.tar.gz
fi
if [ ! -f cmake-$CMAKE_VERSION.tar.gz ]; then
wget https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION.tar.gz
fi
if [ ! -f swig-$SWIG_VERSION.tar.gz ]; then
wget https://github.com/swig/swig/archive/rel-$SWIG_VERSION.tar.gz -O swig-$SWIG_VERSION.tar.gz
fi
if [ ! -f cppcheck-$CPPCHECK_VERSION.tar.gz ]; then
wget https://github.com/danmar/cppcheck/archive/$CPPCHECK_VERSION.tar.gz -O cppcheck-$CPPCHECK_VERSION.tar.gz
fi
if [ ! -f llvm-$LLVM_VERSION.src.tar.xz ]; then
wget https://releases.llvm.org/$LLVM_VERSION/llvm-$LLVM_VERSION.src.tar.xz
wget https://releases.llvm.org/$LLVM_VERSION/cfe-$LLVM_VERSION.src.tar.xz
wget https://releases.llvm.org/$LLVM_VERSION/lld-$LLVM_VERSION.src.tar.xz
wget https://releases.llvm.org/$LLVM_VERSION/compiler-rt-$LLVM_VERSION.src.tar.xz
wget https://releases.llvm.org/$LLVM_VERSION/clang-tools-extra-$LLVM_VERSION.src.tar.xz
fi
if [ ! -f pahole-gdb-master.zip ]; then
wget https://github.com/PhilArmstrong/pahole-gdb/archive/master.zip -O pahole-gdb-master.zip
fi
# verify all archives
# NOTE: Verification can fail if the archive is signed by another developer. I
# haven't added commands to download all developer GnuPG keys because the
# download is very slow. If the verification fails for you, figure out who has
# signed the archive and download their public key instead.
GPG="gpg --homedir .gnupg"
KEYSERVER="hkp://keyserver.ubuntu.com"
mkdir -p .gnupg
chmod 700 .gnupg
# verify gcc
if [ ! -f gcc-$GCC_VERSION.tar.gz.sig ]; then
wget https://ftp.gnu.org/gnu/gcc/gcc-$GCC_VERSION/gcc-$GCC_VERSION.tar.gz.sig
fi
# list of valid gcc gnupg keys: https://gcc.gnu.org/mirrors.html
$GPG --keyserver $KEYSERVER --recv-keys 0xA328C3A2C3C45C06
$GPG --verify gcc-$GCC_VERSION.tar.gz.sig gcc-$GCC_VERSION.tar.gz
# verify binutils
if [ ! -f binutils-$BINUTILS_VERSION.tar.gz.sig ]; then
wget https://ftp.gnu.org/gnu/binutils/binutils-$BINUTILS_VERSION.tar.gz.sig
fi
$GPG --keyserver $KEYSERVER --recv-keys 0xDD9E3C4F
$GPG --verify binutils-$BINUTILS_VERSION.tar.gz.sig binutils-$BINUTILS_VERSION.tar.gz
# verify gdb
if [ ! -f gdb-$GDB_VERSION.tar.gz.sig ]; then
wget https://ftp.gnu.org/gnu/gdb/gdb-$GDB_VERSION.tar.gz.sig
fi
$GPG --keyserver $KEYSERVER --recv-keys 0xFF325CF3
$GPG --verify gdb-$GDB_VERSION.tar.gz.sig gdb-$GDB_VERSION.tar.gz
# verify cmake
if [ ! -f cmake-$CMAKE_VERSION-SHA-256.txt ] || [ ! -f cmake-$CMAKE_VERSION-SHA-256.txt.asc ]; then
wget https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-SHA-256.txt
wget https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-SHA-256.txt.asc
# Because CentOS 7 doesn't have the `--ignore-missing` flag for `sha256sum`
# we filter out the missing files from the sums here manually.
cat cmake-$CMAKE_VERSION-SHA-256.txt | grep "cmake-$CMAKE_VERSION.tar.gz" > cmake-$CMAKE_VERSION-SHA-256-filtered.txt
fi
$GPG --keyserver $KEYSERVER --recv-keys 0xC6C265324BBEBDC350B513D02D2CEF1034921684
sha256sum -c cmake-$CMAKE_VERSION-SHA-256-filtered.txt
$GPG --verify cmake-$CMAKE_VERSION-SHA-256.txt.asc cmake-$CMAKE_VERSION-SHA-256.txt
# verify llvm, cfe, lld, clang-tools-extra
if [ ! -f llvm-$LLVM_VERSION.src.tar.xz.sig ]; then
wget https://releases.llvm.org/$LLVM_VERSION/llvm-$LLVM_VERSION.src.tar.xz.sig
wget https://releases.llvm.org/$LLVM_VERSION/cfe-$LLVM_VERSION.src.tar.xz.sig
wget https://releases.llvm.org/$LLVM_VERSION/lld-$LLVM_VERSION.src.tar.xz.sig
wget https://releases.llvm.org/$LLVM_VERSION/compiler-rt-$LLVM_VERSION.src.tar.xz.sig
wget https://releases.llvm.org/$LLVM_VERSION/clang-tools-extra-$LLVM_VERSION.src.tar.xz.sig
fi
# list of valid llvm gnupg keys: https://releases.llvm.org/download.html
$GPG --keyserver $KEYSERVER --recv-keys 0x345AD05D
$GPG --verify llvm-$LLVM_VERSION.src.tar.xz.sig llvm-$LLVM_VERSION.src.tar.xz
$GPG --verify cfe-$LLVM_VERSION.src.tar.xz.sig cfe-$LLVM_VERSION.src.tar.xz
$GPG --verify lld-$LLVM_VERSION.src.tar.xz.sig lld-$LLVM_VERSION.src.tar.xz
$GPG --verify compiler-rt-$LLVM_VERSION.src.tar.xz.sig compiler-rt-$LLVM_VERSION.src.tar.xz
$GPG --verify clang-tools-extra-$LLVM_VERSION.src.tar.xz.sig clang-tools-extra-$LLVM_VERSION.src.tar.xz
popd
# create build directory
mkdir -p build
pushd build
# compile gcc
if [ ! -f $PREFIX/bin/gcc ]; then
if [ -d gcc-$GCC_VERSION ]; then
rm -rf gcc-$GCC_VERSION
fi
tar -xvf ../archives/gcc-$GCC_VERSION.tar.gz
pushd gcc-$GCC_VERSION
./contrib/download_prerequisites
mkdir build && pushd build
# influenced by: https://buildd.debian.org/status/fetch.php?pkg=gcc-8&arch=amd64&ver=8.3.0-6&stamp=1554588545
../configure -v \
--build=x86_64-linux-gnu \
--host=x86_64-linux-gnu \
--target=x86_64-linux-gnu \
--prefix=$PREFIX \
--disable-multilib \
--with-system-zlib \
--enable-checking=release \
--enable-languages=c,c++,fortran \
--enable-gold=yes \
--enable-ld=yes \
--enable-lto \
--enable-bootstrap \
--disable-vtable-verify \
--disable-werror \
--without-included-gettext \
--enable-threads=posix \
--enable-nls \
--enable-clocale=gnu \
--enable-libstdcxx-debug \
--enable-libstdcxx-time=yes \
--enable-gnu-unique-object \
--enable-libmpx \
--enable-plugin \
--enable-default-pie \
--with-target-system-zlib \
--with-tune=generic \
--without-cuda-driver
#--program-suffix=$( printf "$GCC_VERSION" | cut -d '.' -f 1,2 ) \
make -j$CPUS
# make -k check # run test suite
make install
popd && popd
fi
# activate toolchain
export PATH=$PREFIX/bin:$PATH
export LD_LIBRARY_PATH=$PREFIX/lib64
# compile binutils
if [ ! -f $PREFIX/bin/ld.gold ]; then
if [ -d binutils-$BINUTILS_VERSION ]; then
rm -rf binutils-$BINUTILS_VERSION
fi
tar -xvf ../archives/binutils-$BINUTILS_VERSION.tar.gz
pushd binutils-$BINUTILS_VERSION
mkdir build && pushd build
# influenced by: https://buildd.debian.org/status/fetch.php?pkg=binutils&arch=amd64&ver=2.32-7&stamp=1553247092
env \
CC=gcc \
CXX=g++ \
CFLAGS="-g -O2" \
CXXFLAGS="-g -O2" \
LDFLAGS="" \
../configure \
--build=x86_64-linux-gnu \
--host=x86_64-linux-gnu \
--prefix=$PREFIX \
--enable-ld=default \
--enable-gold \
--enable-lto \
--enable-plugins \
--enable-shared \
--enable-threads \
--with-system-zlib \
--enable-deterministic-archives \
--disable-compressed-debug-sections \
--enable-new-dtags \
--disable-werror
make -j$CPUS
# make -k check # run test suite
make install
popd && popd
fi
# compile gdb
if [ ! -f $PREFIX/bin/gdb ]; then
if [ -d gdb-$GDB_VERSION ]; then
rm -rf gdb-$GDB_VERSION
fi
tar -xvf ../archives/gdb-$GDB_VERSION.tar.gz
pushd gdb-$GDB_VERSION
mkdir build && pushd build
# https://buildd.debian.org/status/fetch.php?pkg=gdb&arch=amd64&ver=8.2.1-2&stamp=1550831554&raw=0
env \
CC=gcc \
CXX=g++ \
CFLAGS="-g -O2 -fstack-protector-strong -Wformat -Werror=format-security" \
CXXFLAGS="-g -O2 -fstack-protector-strong -Wformat -Werror=format-security" \
CPPFLAGS="-Wdate-time -D_FORTIFY_SOURCE=2 -fPIC" \
LDFLAGS="-Wl,-z,relro" \
PYTHON="" \
../configure \
--build=x86_64-linux-gnu \
--host=x86_64-linux-gnu \
--prefix=$PREFIX \
--disable-maintainer-mode \
--disable-dependency-tracking \
--disable-silent-rules \
--disable-gdbtk \
--disable-shared \
--without-guile \
--with-system-gdbinit=$PREFIX/etc/gdb/gdbinit \
--with-system-readline \
--with-expat \
--with-system-zlib \
--with-lzma \
--with-babeltrace \
--with-intel-pt \
--enable-tui \
--with-python=python3
make -j$CPUS
make install
popd && popd
fi
# install pahole
if [ ! -d $PREFIX/share/pahole-gdb ]; then
unzip ../archives/pahole-gdb-master.zip
mv pahole-gdb-master $PREFIX/share/pahole-gdb
fi
# setup system gdbinit
if [ ! -f $PREFIX/etc/gdb/gdbinit ]; then
mkdir -p $PREFIX/etc/gdb
cat >$PREFIX/etc/gdb/gdbinit <<EOF
# improve formatting
set print pretty on
set print object on
set print static-members on
set print vtbl on
set print demangle on
set demangle-style gnu-v3
set print sevenbit-strings off
# load libstdc++ pretty printers
add-auto-load-scripts-directory $PREFIX/lib64
add-auto-load-safe-path $PREFIX
# load pahole
python
sys.path.insert(0, "$PREFIX/share/pahole-gdb")
import offsets
import pahole
end
EOF
fi
# compile cmake
if [ ! -f $PREFIX/bin/cmake ]; then
if [ -d cmake-$CMAKE_VERSION ]; then
rm -rf cmake-$CMAKE_VERSION
fi
tar -xvf ../archives/cmake-$CMAKE_VERSION.tar.gz
pushd cmake-$CMAKE_VERSION
# influenced by: https://buildd.debian.org/status/fetch.php?pkg=cmake&arch=amd64&ver=3.13.4-1&stamp=1549799837
echo 'set(CMAKE_SKIP_RPATH ON CACHE BOOL "Skip rpath" FORCE)' >> build-flags.cmake
echo 'set(CMAKE_USE_RELATIVE_PATHS ON CACHE BOOL "Use relative paths" FORCE)' >> build-flags.cmake
echo 'set(CMAKE_C_FLAGS "-g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2" CACHE STRING "C flags" FORCE)' >> build-flags.cmake
echo 'set(CMAKE_CXX_FLAGS "-g -O2 -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2" CACHE STRING "C++ flags" FORCE)' >> build-flags.cmake
echo 'set(CMAKE_SKIP_BOOTSTRAP_TEST ON CACHE BOOL "Skip BootstrapTest" FORCE)' >> build-flags.cmake
echo 'set(BUILD_CursesDialog ON CACHE BOOL "Build curses GUI" FORCE)' >> build-flags.cmake
mkdir build && pushd build
../bootstrap \
--prefix=$PREFIX \
--init=../build-flags.cmake \
--parallel=$CPUS \
--system-curl
make -j$CPUS
# make test # run test suite
make install
popd && popd
fi
# compile cppcheck
if [ ! -f $PREFIX/bin/cppcheck ]; then
if [ -d cppcheck-$CPPCHECK_VERSION ]; then
rm -rf cppcheck-$CPPCHECK_VERSION
fi
tar -xvf ../archives/cppcheck-$CPPCHECK_VERSION.tar.gz
pushd cppcheck-$CPPCHECK_VERSION
env \
CC=gcc \
CXX=g++ \
PREFIX=$PREFIX \
CFGDIR=$PREFIX/share/cppcheck/cfg \
make -j$CPUS
env \
CC=gcc \
CXX=g++ \
PREFIX=$PREFIX \
CFGDIR=$PREFIX/share/cppcheck/cfg \
make install
popd
fi
# compile swig
if [ ! -d swig-$SWIG_VERSION/install ]; then
if [ -d swig-$SWIG_VERSION ]; then
rm -rf swig-$SWIG_VERSION
fi
tar -xvf ../archives/swig-$SWIG_VERSION.tar.gz
mv swig-rel-$SWIG_VERSION swig-$SWIG_VERSION
pushd swig-$SWIG_VERSION
./autogen.sh
mkdir build && pushd build
../configure --prefix=$DIR/build/swig-$SWIG_VERSION/install
make -j$CPUS
make install
popd && popd
fi
# compile llvm
if [ ! -f $PREFIX/bin/clang ]; then
if [ -d llvm-$LLVM_VERSION ]; then
rm -rf llvm-$LLVM_VERSION
fi
tar -xvf ../archives/llvm-$LLVM_VERSION.src.tar.xz
mv llvm-$LLVM_VERSION.src llvm-$LLVM_VERSION
tar -xvf ../archives/cfe-$LLVM_VERSION.src.tar.xz
mv cfe-$LLVM_VERSION.src llvm-$LLVM_VERSION/tools/clang
tar -xvf ../archives/lld-$LLVM_VERSION.src.tar.xz
mv lld-$LLVM_VERSION.src/ llvm-$LLVM_VERSION/tools/lld
tar -xvf ../archives/compiler-rt-$LLVM_VERSION.src.tar.xz
mv compiler-rt-$LLVM_VERSION.src/ llvm-$LLVM_VERSION/projects/compiler-rt
tar -xvf ../archives/clang-tools-extra-$LLVM_VERSION.src.tar.xz
mv clang-tools-extra-$LLVM_VERSION.src/ llvm-$LLVM_VERSION/tools/clang/tools/extra
pushd llvm-$LLVM_VERSION
mkdir build && pushd build
# activate swig
export PATH=$DIR/build/swig-$SWIG_VERSION/install/bin:$PATH
# influenced by: https://buildd.debian.org/status/fetch.php?pkg=llvm-toolchain-7&arch=amd64&ver=1%3A7.0.1%7E%2Brc2-1%7Eexp1&stamp=1541506173&raw=0
cmake .. \
-DGCC_INSTALL_PREFIX=$PREFIX \
-DCMAKE_C_COMPILER=$PREFIX/bin/gcc \
-DCMAKE_CXX_COMPILER=$PREFIX/bin/g++ \
-DCMAKE_CXX_LINK_FLAGS="-L$PREFIX/lib64 -Wl,-rpath,$PREFIX/lib64" \
-DCMAKE_INSTALL_PREFIX=$PREFIX \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_CXX_FLAGS_RELWITHDEBINFO="-O2 -DNDEBUG" \
-DCMAKE_CXX_FLAGS=' -fuse-ld=gold -fPIC -Wno-unused-command-line-argument -Wno-unknown-warning-option' \
-DCMAKE_C_FLAGS=' -fuse-ld=gold -fPIC -Wno-unused-command-line-argument -Wno-unknown-warning-option' \
-DLLVM_LINK_LLVM_DYLIB=ON \
-DLLVM_INSTALL_UTILS=ON \
-DLLVM_VERSION_SUFFIX= \
-DLLVM_BUILD_LLVM_DYLIB=ON \
-DLLVM_ENABLE_RTTI=ON \
-DLLVM_ENABLE_FFI=ON \
-DLLVM_BINUTILS_INCDIR=$PREFIX/include/ \
-DLLVM_USE_PERF=yes \
-DLIBCLANG_LIBRARY_VERSION=1 \
-DCLANG_ENABLE_BOOTSTRAP=ON
make -j$CPUS
make -j$CPUS check-clang # run clang test suite
make -j$CPUS check-lld # run lld test suite
make install
popd && popd
fi
# create README
if [ ! -f $PREFIX/README.md ]; then
cat >$PREFIX/README.md <<EOF
# Memgraph Toolchain v$TOOLCHAIN_VERSION
## Included tools
- GCC $GCC_VERSION
- Binutils $BINUTILS_VERSION
- GDB $GDB_VERSION
- CMake $CMAKE_VERSION
- Cppcheck $CPPCHECK_VERSION
- LLVM (Clang, LLD, compiler-rt, Clang tools extra) $LLVM_VERSION
## Required libraries
In order to be able to run all of these tools you should install the following
packages:
\`\`\`
$DEPS_MANAGER install ${DEPS_RUN[@]}
\`\`\`
## Usage
In order to use the toolchain you just have to source the activation script:
\`\`\`
source $PREFIX/activate
\`\`\`
EOF
fi
# create activation script
if [ ! -f $PREFIX/activate ]; then
cat >$PREFIX/activate <<EOF
# This file must be used with "source $PREFIX/activate" *from bash*
# You can't run it directly!
# check for active virtual environments
if [ "\$( type -t deactivate )" != "" ]; then
echo "You already have an active virtual environment!"
return 0
fi
# check that we aren't root
if [ "\$USER" == "root" ]; then
echo "You shouldn't use the toolchan as root!"
return 0
fi
# save original environment
export ORIG_PATH=\$PATH
export ORIG_PS1=\$PS1
export ORIG_LD_LIBRARY_PATH=\$LD_LIBRARY_PATH
# activate new environment
export PATH=$PREFIX/bin:\$PATH
export PS1="(TOOLCHAIN) \$PS1"
export LD_LIBRARY_PATH=$PREFIX/lib:$PREFIX/lib64
# disable root
function su () {
echo "You don't want to use root functions while using the toolchain!"
return 1
}
function sudo () {
echo "You don't want to use root functions while using the toolchain!"
return 1
}
# create deactivation function
function deactivate() {
export PATH=\$ORIG_PATH
export PS1=\$ORIG_PS1
export LD_LIBRARY_PATH=\$ORIG_LD_LIBRARY_PATH
unset ORIG_PATH ORIG_PS1 ORIG_LD_LIBRARY_PATH
unset -f su sudo deactivate
}
EOF
fi
# create toolchain archive
if [ ! -f $NAME-binaries-$DISTRO.tar.gz ]; then
tar --owner=root --group=root -cpvzf $NAME-binaries-$DISTRO.tar.gz -C /opt $NAME
fi
# output final instructions
echo -e "\n\n"
echo "All tools have been built. They are installed in '$PREFIX'."
echo "In order to distribute the tools to someone else, an archive with the toolchain was created in the 'build' directory."
echo "If you want to install the packed tools you should execute the following command:"
echo
echo " tar -xvzf build/$NAME-binaries.tar.gz -C /opt"
echo
echo "Because the tools were built on this machine, you should probably change the permissions of the installation directory using:"
echo
echo " OPTIONAL: chown -R root:root $PREFIX"
echo
echo "In order to use all of the newly compiled tools you should use the prepared activation script:"
echo
echo " source $PREFIX/activate"
echo
echo "Or, for more advanced uses, you can add the following lines to your script:"
echo
echo " export PATH=$PREFIX/bin:\$PATH"
echo " export LD_LIBRARY_PATH=$PREFIX/lib:$PREFIX/lib64"
echo
echo "Enjoy!"

View File

@@ -4,88 +4,36 @@
pushd () { command pushd "$@" > /dev/null; }
popd () { command popd "$@" > /dev/null; }
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
CPUS=$( cat /proc/cpuinfo | grep processor | wc -l )
CPUS=$( grep -c processor < /proc/cpuinfo )
cd "$DIR"
source "$DIR/../util.sh"
DISTRO="$(operating_system)"
# toolchain version
TOOLCHAIN_VERSION=1
TOOLCHAIN_VERSION=2
# package versions used
GCC_VERSION=8.3.0
BINUTILS_VERSION=2.32
GDB_VERSION=8.2.1
CMAKE_VERSION=3.14.2
CPPCHECK_VERSION=1.87
LLVM_VERSION=8.0.0
SWIG_VERSION=3.0.12 # used only for LLVM compilation
GCC_VERSION=10.2.0
BINUTILS_VERSION=2.35.1
case "$DISTRO" in
centos-7) # because GDB >= 9 does NOT compile with readline6.
GDB_VERSION=8.3
;;
*)
GDB_VERSION=10.1
;;
esac
CMAKE_VERSION=3.18.4
CPPCHECK_VERSION=2.2
LLVM_VERSION=11.0.0
SWIG_VERSION=4.0.2 # used only for LLVM compilation
# check for installed dependencies
DISTRO="$( egrep '^(VERSION_)?ID=' /etc/os-release | sort | cut -d '=' -f 2- | sed 's/"//g' | paste -s -d '-' )"
if [ "$DISTRO" == "debian-9" ] || [ "$DISTRO" == "ubuntu-18.04" ]; then
DEPS_MANAGER=apt-get
DEPS_COMPILE=(
coreutils gcc g++ build-essential make # generic build tools
wget # used for archive download
gnupg # used for archive signature verification
tar gzip bzip2 xz-utils # used for archive unpacking
zlib1g-dev # zlib library used for all builds
libexpat1-dev libipt-dev libbabeltrace-dev liblzma-dev python3-dev texinfo # for gdb
libreadline-dev # for cmake and llvm
libffi-dev libxml2-dev # for llvm
libedit-dev libpcre3-dev automake bison # for swig
)
DEPS_RUN=(
make # generic build tools
tar gzip bzip2 xz-utils # used for archive unpacking
zlib1g # zlib library used for all builds
libexpat1 libipt1 libbabeltrace1 liblzma5 python3 # for gdb
libreadline7 # for cmake and llvm
libffi6 libxml2 # for llvm
)
elif [ "$DISTRO" == "centos-7" ]; then
DEPS_MANAGER=yum
DEPS_COMPILE=(
coreutils gcc gcc-c++ make # generic build tools
wget # used for archive download
gnupg2 # used for archive signature verification
tar gzip bzip2 xz # used for archive unpacking
zlib-devel # zlib library used for all builds
expat-devel libipt-devel libbabeltrace-devel xz-devel python36-devel texinfo # for gdb
readline-devel # for cmake and llvm
libffi-devel libxml2-devel # for llvm
libedit-devel pcre-devel automake bison # for swig
)
DEPS_RUN=(
make # generic build tools
tar gzip bzip2 xz # used for archive unpacking
zlib # zlib library used for all builds
expat libipt libbabeltrace xz-libs python36 # for gdb
readline # for cmake and llvm
libffi libxml2 # for llvm
)
else
echo "Unknown distribution: $DISTRO!"
exit 1
fi
missing=""
for dep in ${DEPS_COMPILE[@]}; do
if [ "$DEPS_MANAGER" == "apt-get" ]; then
if ! dpkg -s $dep >/dev/null 2>/dev/null; then
missing="$dep $missing"
fi
elif [ "$DEPS_MANAGER" == "yum" ]; then
if ! yum list installed $dep >/dev/null 2>/dev/null; then
missing="$dep $missing"
fi
else
echo "Invalid package manager: $DEPS_MANAGER!"
exit 1
fi
done
if [ "$missing" != "" ]; then
echo "Missing dependencies: $missing"
exit 1
fi
# Check for the dependencies.
echo "ALL BUILD PACKAGES: $($DIR/../os/$DISTRO.sh list TOOLCHAIN_BUILD_DEPS)"
$DIR/../os/$DISTRO.sh check TOOLCHAIN_BUILD_DEPS
echo "ALL RUN PACKAGES: $($DIR/../os/$DISTRO.sh list TOOLCHAIN_RUN_DEPS)"
$DIR/../os/$DISTRO.sh check TOOLCHAIN_RUN_DEPS
# check installation directory
NAME=toolchain-v$TOOLCHAIN_VERSION
@@ -136,10 +84,14 @@ if [ ! -f cppcheck-$CPPCHECK_VERSION.tar.gz ]; then
wget https://github.com/danmar/cppcheck/archive/$CPPCHECK_VERSION.tar.gz -O cppcheck-$CPPCHECK_VERSION.tar.gz
fi
if [ ! -f llvm-$LLVM_VERSION.src.tar.xz ]; then
wget http://releases.llvm.org/$LLVM_VERSION/llvm-$LLVM_VERSION.src.tar.xz
wget http://releases.llvm.org/$LLVM_VERSION/cfe-$LLVM_VERSION.src.tar.xz
wget http://releases.llvm.org/$LLVM_VERSION/lld-$LLVM_VERSION.src.tar.xz
wget http://releases.llvm.org/$LLVM_VERSION/clang-tools-extra-$LLVM_VERSION.src.tar.xz
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/llvm-$LLVM_VERSION.src.tar.xz
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/clang-$LLVM_VERSION.src.tar.xz
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/lld-$LLVM_VERSION.src.tar.xz
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/clang-tools-extra-$LLVM_VERSION.src.tar.xz
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/compiler-rt-$LLVM_VERSION.src.tar.xz
fi
if [ ! -f pahole-gdb-master.zip ]; then
wget https://github.com/PhilArmstrong/pahole-gdb/archive/master.zip -O pahole-gdb-master.zip
fi
# verify all archives
@@ -156,7 +108,7 @@ if [ ! -f gcc-$GCC_VERSION.tar.gz.sig ]; then
wget https://ftp.gnu.org/gnu/gcc/gcc-$GCC_VERSION/gcc-$GCC_VERSION.tar.gz.sig
fi
# list of valid gcc gnupg keys: https://gcc.gnu.org/mirrors.html
$GPG --keyserver $KEYSERVER --recv-keys 0xC3C45C06
$GPG --keyserver $KEYSERVER --recv-keys 0x3AB00996FC26A641
$GPG --verify gcc-$GCC_VERSION.tar.gz.sig gcc-$GCC_VERSION.tar.gz
# verify binutils
if [ ! -f binutils-$BINUTILS_VERSION.tar.gz.sig ]; then
@@ -174,23 +126,28 @@ $GPG --verify gdb-$GDB_VERSION.tar.gz.sig gdb-$GDB_VERSION.tar.gz
if [ ! -f cmake-$CMAKE_VERSION-SHA-256.txt ] || [ ! -f cmake-$CMAKE_VERSION-SHA-256.txt.asc ]; then
wget https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-SHA-256.txt
wget https://github.com/Kitware/CMake/releases/download/v$CMAKE_VERSION/cmake-$CMAKE_VERSION-SHA-256.txt.asc
# Because CentOS 7 doesn't have the `--ignore-missing` flag for `sha256sum`
# we filter out the missing files from the sums here manually.
cat cmake-$CMAKE_VERSION-SHA-256.txt | grep "cmake-$CMAKE_VERSION.tar.gz" > cmake-$CMAKE_VERSION-SHA-256-filtered.txt
fi
$GPG --keyserver $KEYSERVER --recv-keys 0x7BFB4EDA
sha256sum --ignore-missing -c cmake-$CMAKE_VERSION-SHA-256.txt
$GPG --keyserver $KEYSERVER --recv-keys 0xC6C265324BBEBDC350B513D02D2CEF1034921684
sha256sum -c cmake-$CMAKE_VERSION-SHA-256-filtered.txt
$GPG --verify cmake-$CMAKE_VERSION-SHA-256.txt.asc cmake-$CMAKE_VERSION-SHA-256.txt
# verify llvm, cfe, lld, clang-tools-extra
if [ ! -f llvm-$LLVM_VERSION.src.tar.xz.sig ]; then
wget http://releases.llvm.org/$LLVM_VERSION/llvm-$LLVM_VERSION.src.tar.xz.sig
wget http://releases.llvm.org/$LLVM_VERSION/cfe-$LLVM_VERSION.src.tar.xz.sig
wget http://releases.llvm.org/$LLVM_VERSION/lld-$LLVM_VERSION.src.tar.xz.sig
wget http://releases.llvm.org/$LLVM_VERSION/clang-tools-extra-$LLVM_VERSION.src.tar.xz.sig
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/llvm-$LLVM_VERSION.src.tar.xz.sig
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/clang-$LLVM_VERSION.src.tar.xz.sig
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/lld-$LLVM_VERSION.src.tar.xz.sig
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/clang-tools-extra-$LLVM_VERSION.src.tar.xz.sig
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-$LLVM_VERSION/compiler-rt-$LLVM_VERSION.src.tar.xz.sig
fi
# list of valid llvm gnupg keys: https://releases.llvm.org/download.html
$GPG --keyserver $KEYSERVER --recv-keys 0x345AD05D
$GPG --verify llvm-$LLVM_VERSION.src.tar.xz.sig llvm-$LLVM_VERSION.src.tar.xz
$GPG --verify cfe-$LLVM_VERSION.src.tar.xz.sig cfe-$LLVM_VERSION.src.tar.xz
$GPG --verify clang-$LLVM_VERSION.src.tar.xz.sig clang-$LLVM_VERSION.src.tar.xz
$GPG --verify lld-$LLVM_VERSION.src.tar.xz.sig lld-$LLVM_VERSION.src.tar.xz
$GPG --verify clang-tools-extra-$LLVM_VERSION.src.tar.xz.sig clang-tools-extra-$LLVM_VERSION.src.tar.xz
$GPG --verify compiler-rt-$LLVM_VERSION.src.tar.xz.sig compiler-rt-$LLVM_VERSION.src.tar.xz
popd
# create build directory
@@ -309,6 +266,7 @@ if [ ! -f $PREFIX/bin/gdb ]; then
--disable-gdbtk \
--disable-shared \
--without-guile \
--with-system-gdbinit=$PREFIX/etc/gdb/gdbinit \
--with-system-readline \
--with-expat \
--with-system-zlib \
@@ -322,6 +280,38 @@ if [ ! -f $PREFIX/bin/gdb ]; then
popd && popd
fi
# install pahole
if [ ! -d $PREFIX/share/pahole-gdb ]; then
unzip ../archives/pahole-gdb-master.zip
mv pahole-gdb-master $PREFIX/share/pahole-gdb
fi
# setup system gdbinit
if [ ! -f $PREFIX/etc/gdb/gdbinit ]; then
mkdir -p $PREFIX/etc/gdb
cat >$PREFIX/etc/gdb/gdbinit <<EOF
# improve formatting
set print pretty on
set print object on
set print static-members on
set print vtbl on
set print demangle on
set demangle-style gnu-v3
set print sevenbit-strings off
# load libstdc++ pretty printers
add-auto-load-scripts-directory $PREFIX/lib64
add-auto-load-safe-path $PREFIX
# load pahole
python
sys.path.insert(0, "$PREFIX/share/pahole-gdb")
import offsets
import pahole
end
EOF
fi
# compile cmake
if [ ! -f $PREFIX/bin/cmake ]; then
if [ -d cmake-$CMAKE_VERSION ]; then
@@ -340,7 +330,8 @@ if [ ! -f $PREFIX/bin/cmake ]; then
../bootstrap \
--prefix=$PREFIX \
--init=../build-flags.cmake \
--parallel=$CPUS
--parallel=$CPUS \
--system-curl
make -j$CPUS
# make test # run test suite
make install
@@ -358,12 +349,14 @@ if [ ! -f $PREFIX/bin/cppcheck ]; then
CC=gcc \
CXX=g++ \
PREFIX=$PREFIX \
FILESDIR=$PREFIX/share/cppcheck \
CFGDIR=$PREFIX/share/cppcheck/cfg \
make -j$CPUS
env \
CC=gcc \
CXX=g++ \
PREFIX=$PREFIX \
FILESDIR=$PREFIX/share/cppcheck \
CFGDIR=$PREFIX/share/cppcheck/cfg \
make install
popd
@@ -392,19 +385,20 @@ if [ ! -f $PREFIX/bin/clang ]; then
fi
tar -xvf ../archives/llvm-$LLVM_VERSION.src.tar.xz
mv llvm-$LLVM_VERSION.src llvm-$LLVM_VERSION
tar -xvf ../archives/cfe-$LLVM_VERSION.src.tar.xz
mv cfe-$LLVM_VERSION.src llvm-$LLVM_VERSION/tools/clang
tar -xvf ../archives/clang-$LLVM_VERSION.src.tar.xz
mv clang-$LLVM_VERSION.src llvm-$LLVM_VERSION/tools/clang
tar -xvf ../archives/lld-$LLVM_VERSION.src.tar.xz
mv lld-$LLVM_VERSION.src/ llvm-$LLVM_VERSION/tools/lld
tar -xvf ../archives/clang-tools-extra-$LLVM_VERSION.src.tar.xz
mv clang-tools-extra-$LLVM_VERSION.src/ llvm-$LLVM_VERSION/tools/clang/tools/extra
tar -xvf ../archives/compiler-rt-$LLVM_VERSION.src.tar.xz
mv compiler-rt-$LLVM_VERSION.src/ llvm-$LLVM_VERSION/projects/compiler-rt
pushd llvm-$LLVM_VERSION
mkdir build && pushd build
# activate swig
export PATH=$DIR/build/swig-$SWIG_VERSION/install/bin:$PATH
# influenced by: https://buildd.debian.org/status/fetch.php?pkg=llvm-toolchain-7&arch=amd64&ver=1%3A7.0.1%7E%2Brc2-1%7Eexp1&stamp=1541506173&raw=0
cmake .. \
-DGCC_INSTALL_PREFIX=$PREFIX \
-DCMAKE_C_COMPILER=$PREFIX/bin/gcc \
-DCMAKE_CXX_COMPILER=$PREFIX/bin/g++ \
-DCMAKE_CXX_LINK_FLAGS="-L$PREFIX/lib64 -Wl,-rpath,$PREFIX/lib64" \
@@ -420,9 +414,7 @@ if [ ! -f $PREFIX/bin/clang ]; then
-DLLVM_ENABLE_RTTI=ON \
-DLLVM_ENABLE_FFI=ON \
-DLLVM_BINUTILS_INCDIR=$PREFIX/include/ \
-DLLVM_USE_PERF=yes \
-DLIBCLANG_LIBRARY_VERSION=1 \
-DCLANG_ENABLE_BOOTSTRAP=ON
-DLLVM_USE_PERF=yes
make -j$CPUS
make -j$CPUS check-clang # run clang test suite
make -j$CPUS check-lld # run lld test suite
@@ -442,7 +434,7 @@ if [ ! -f $PREFIX/README.md ]; then
- GDB $GDB_VERSION
- CMake $CMAKE_VERSION
- Cppcheck $CPPCHECK_VERSION
- LLVM (Clang, LLD, Clang tools extra) $LLVM_VERSION
- LLVM (Clang, LLD, compiler-rt, Clang tools extra) $LLVM_VERSION
## Required libraries
@@ -450,7 +442,7 @@ In order to be able to run all of these tools you should install the following
packages:
\`\`\`
$DEPS_MANAGER install ${DEPS_RUN[@]}
$($DIR/../os/$DISTRO.sh list TOOLCHAIN_RUN_DEPS)
\`\`\`
## Usage
@@ -488,7 +480,7 @@ export ORIG_LD_LIBRARY_PATH=\$LD_LIBRARY_PATH
# activate new environment
export PATH=$PREFIX/bin:\$PATH
export PS1="(TOOLCHAIN) \$PS1"
export PS1="($NAME) \$PS1"
export LD_LIBRARY_PATH=$PREFIX/lib:$PREFIX/lib64
# disable root

45
environment/util.sh Normal file
View File

@@ -0,0 +1,45 @@
#!/bin/bash
operating_system() {
grep -E '^(VERSION_)?ID=' /etc/os-release | \
sort | cut -d '=' -f 2- | sed 's/"//g' | paste -s -d '-'
}
check_all_yum() {
local missing=""
for pkg in $1; do
if ! yum list installed "$pkg" >/dev/null 2>/dev/null; then
missing="$pkg $missing"
fi
done
if [ "$missing" != "" ]; then
echo "MISSING PACKAGES: $missing"
exit 1
fi
}
check_all_dpkg() {
local missing=""
for pkg in $1; do
if ! dpkg -s "$pkg" >/dev/null 2>/dev/null; then
missing="$pkg $missing"
fi
done
if [ "$missing" != "" ]; then
echo "MISSING PACKAGES: $missing"
exit 1
fi
}
check_all_dnf() {
local missing=""
for pkg in $1; do
if ! dnf list installed "$pkg" >/dev/null 2>/dev/null; then
missing="$pkg $missing"
fi
done
if [ "$missing" != "" ]; then
echo "MISSING PACKAGES: $missing"
exit 1
fi
}

View File

@@ -1,2 +0,0 @@
# distributed
add_subdirectory(distributed)

View File

@@ -1,8 +0,0 @@
---
Language: Cpp
BasedOnStyle: Google
Standard: "C++11"
UseTab: Never
DerivePointerAlignment: false
PointerAlignment: Right
...

View File

@@ -1,7 +0,0 @@
*.out
*.pyc
main
libs/
*.cereal
*.backup
*.out

View File

@@ -1,42 +0,0 @@
project(distributed)
# set directory variables
set(src_dir ${PROJECT_SOURCE_DIR}/src)
set(libs_dir ${PROJECT_SOURCE_DIR}/libs)
# includes
include_directories(SYSTEM ${libs_dir}/cereal/include)
include_directories(${src_dir})
# library from distributed sources
file(GLOB_RECURSE src_files ${src_dir}/*.cpp)
add_library(distributed_lib STATIC ${src_files})
## distributed Memgraph executable
set(executable_name main)
add_executable(${executable_name} ${PROJECT_SOURCE_DIR}/main.cpp)
target_link_libraries(${executable_name} distributed_lib memgraph_lib)
## dummy distributed Memgraph client
set(executable_name main-client)
add_executable(${executable_name} ${PROJECT_SOURCE_DIR}/main-client.cpp)
target_link_libraries(${executable_name} distributed_lib memgraph_lib)
# tests
enable_testing()
add_subdirectory(tests)
# copy test scripts into the build/ directory (for distributed tests)
configure_file(${PROJECT_SOURCE_DIR}/tests/start_distributed
${PROJECT_BINARY_DIR}/tests/start_distributed COPYONLY)
configure_file(${PROJECT_SOURCE_DIR}/tests/config
${PROJECT_BINARY_DIR}/tests/config COPYONLY)
# copy main scripts into build/ directory (for distributed Memgraph)
configure_file(${PROJECT_SOURCE_DIR}/start_main.py
${PROJECT_BINARY_DIR}/start_main.py COPYONLY)
configure_file(${PROJECT_SOURCE_DIR}/config
${PROJECT_BINARY_DIR}/config COPYONLY)

View File

@@ -1,35 +0,0 @@
# distributed memgraph
This subdirectory structure implements distributed infrastructure of Memgraph.
## Terminology
* Memgraph Node Id (mnid): a machine (processs) that runs a (distributed) Memgraph program.
* Node: a computer that performs (distributed) work.
* Vertex: an abstract graph concept.
* Reactor: a unit of concurrent execution, lives on its own thread.
* Channel: a (one-way) communication abstraction between Reactors. The reactors can be on the same machine or on different processes.
* Message: gets sent through channels. Must be serializable if sent via network layer (library: cereal).
* Event: arrival of a (subclass of) Message. You can register callbacks. Register exact callbacks (not for derivated/subclasses).
* EventStream: read-end of a channel, is owned by exactly one Reactor/thread.
* ChannelWriter: write-end of a channel, can be owned (wrote into) by multiple threads.
## Ownership:
* System, Distributed are singletons. They should be always alive.
* ChannelWriter (write-end) should be lightweight and can be copied arbitrarily.
* EventStream (read-end) should never be written by anyone except the owner (the reactor that created it).
* In general: always think about who owns an object. Preferably write it in its comment block.
## Code Conventions
* Locked: A method having a "Locked..." prefix indicates that you
have to lock the appropriate mutex before calling this function.
* ALWAYS close channels. You will memory leak if you don't.
Reactor::CloseChannel or Subscription::Close will do the trick.
## Dependencies
* cereal
* <other memgraph dependencies>

View File

@@ -1,3 +0,0 @@
0 127.0.0.1 10010
1 127.0.0.1 10011
2 127.0.0.1 10012

View File

@@ -1,13 +0,0 @@
#!/usr/bin/env bash
working_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
libs_dir=$working_dir/libs
if [ ! -d $libs_dir ]; then
mkdir $libs_dir
fi
cd $libs_dir
git clone https://github.com/USCiLab/cereal.git
cd $libs_dir/cereal
git checkout v1.2.2

View File

@@ -1,77 +0,0 @@
#include <fstream>
#include <iostream>
#include <memory>
#include "memgraph_config.hpp"
#include "memgraph_distributed.hpp"
#include "memgraph_transactions.hpp"
#include "reactors_distributed.hpp"
/**
* List of queries that should be executed.
*/
std::vector<std::string> queries = {
{"create vertex", "create vertex", "create vertex", "create vertex",
"create vertex", "create vertex", "create vertex", "create vertex",
"create vertex", "create vertex", "vertex count", "create vertex",
"create vertex", "vertex count"}};
/**
* This is the client that issues some hard-coded queries.
*/
class Client : public Reactor {
public:
Client(std::string name) : Reactor(name) {}
void IssueQueries(std::shared_ptr<ChannelWriter> channel_to_leader) {
// (concurrently) create a couple of vertices
for (int query_idx = 0; query_idx < static_cast<int64_t>(queries.size());
++query_idx) {
// register callback
std::string channel_name = "query-" + std::to_string(query_idx);
auto stream = Open(channel_name).first;
stream->OnEventOnce().ChainOnce<ResultMsg>(
[this, query_idx](const ResultMsg &msg, const Subscription &sub) {
std::cout << "Result of query " << query_idx << " ("
<< queries[query_idx] << "):" << std::endl
<< " " << msg.result() << std::endl;
sub.CloseChannel();
});
// then issue the query (to avoid race conditions)
std::cout << "Issuing command " << query_idx << " (" << queries[query_idx]
<< ")" << std::endl;
channel_to_leader->Send<QueryMsg>(channel_name, queries[query_idx]);
}
}
virtual void Run() {
MemgraphDistributed &memgraph = MemgraphDistributed::GetInstance();
auto mnid = memgraph.LeaderMnid();
memgraph.FindChannel(mnid, "master", "client-queries")
->OnEventOnce()
.ChainOnce<ChannelResolvedMessage>(
[this](const ChannelResolvedMessage &msg, const Subscription &sub) {
sub.CloseChannel();
IssueQueries(msg.channelWriter());
});
}
};
int main(int argc, char *argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
System &system = System::GetInstance();
Distributed &distributed = Distributed::GetInstance();
MemgraphDistributed &memgraph = MemgraphDistributed::GetInstance();
memgraph.RegisterConfig(ParseConfig());
distributed.StartServices();
system.Spawn<Client>("client");
system.AwaitShutdown();
distributed.StopServices();
return 0;
}

View File

@@ -1,215 +0,0 @@
#include <fstream>
#include <iostream>
#include <random>
#include <gflags/gflags.h>
#include <glog/logging.h>
#include "memgraph_config.hpp"
#include "memgraph_distributed.hpp"
#include "memgraph_transactions.hpp"
#include "reactors_distributed.hpp"
#include "storage.hpp"
DEFINE_uint64(my_mnid, -1, "Memgraph node id"); // TODO(zuza): this should be assigned by the leader once in the future
class Master : public Reactor {
public:
Master(std::string name, MnidT mnid) : Reactor(name), mnid_(mnid) {
MemgraphDistributed& memgraph = MemgraphDistributed::GetInstance();
worker_mnids_ = memgraph.GetAllMnids();
// remove the leader (itself), because it is not a worker
auto leader_it = std::find(worker_mnids_.begin(), worker_mnids_.end(), memgraph.LeaderMnid());
worker_mnids_.erase(leader_it);
}
virtual void Run() {
Distributed &distributed = Distributed::GetInstance();
std::cout << "Master (" << mnid_ << ") @ " << distributed.network().Address()
<< ":" << distributed.network().Port() << std::endl;
// TODO(zuza): check if all workers are up
// start listening on queries arriving from the client
auto stream = Open("client-queries").first;
stream->OnEvent<QueryMsg>([this](const QueryMsg &msg, const Subscription &){
// process query message
if (msg.query() == "create vertex") {
InstallMakeVertex(msg.GetReturnChannelWriter());
} else if (msg.query() == "vertex count") {
InstallVertexCount(msg.GetReturnChannelWriter());
} else {
std::cerr << "unknown query" << std::endl;
}
});
}
private:
/**
* Organizes communication with all workers and performs VertexCount.
*/
void InstallVertexCount(std::shared_ptr<ChannelWriter> return_channel) {
// open channel through which answers will arrive
auto channel_name = "response" + std::to_string(xid++);
auto result = Open(channel_name).first;
// create struct to keep track of responses
struct VertexCountResponse {
VertexCountResponse(int64_t count, int64_t remaining)
: count_(count), remaining_(remaining) {}
int64_t count_;
int64_t remaining_;
};
// allocate it dynamically so it lives outside the scope of this function
// it will be deallocated once all responses arrive and channel is closed
auto response = std::make_shared<VertexCountResponse>(0, worker_mnids_.size());
// register callbacks
result->OnEvent<ResultQueryVertexCount>(
[this, response, return_channel](const ResultQueryVertexCount &msg,
const Subscription &sub){
response->count_ += msg.count();
--response->remaining_;
if (response->remaining_ == 0) {
sub.CloseChannel();
return_channel->Send<ResultMsg>(std::to_string(response->count_));
}
});
// instruct workers to count vertices
for (auto wmnid : worker_mnids_)
VertexCount(wmnid, channel_name);
}
/**
* Asynchronously counts vertices on the given node.
*
* @param mnid Id of the node whose vertices should be counted.
* @param channel_name Name of the channel on which response will arrive.
*/
void VertexCount(MnidT mnid, std::string channel_name) {
MemgraphDistributed::GetInstance().FindChannel(mnid, "worker", "main")
->OnEventOnceThenClose<ChannelResolvedMessage>(
[this, channel_name](const ChannelResolvedMessage &msg){
msg.channelWriter()->Send<QueryVertexCount>(channel_name);
});
}
/**
* Organizes communication with a random worker and performs MakeVertex.
*/
void InstallMakeVertex(std::shared_ptr<ChannelWriter> return_channel) {
// choose worker on random and instruct it to make vertex
auto wmnid = worker_mnids_[rand() % worker_mnids_.size()];
// open channel through which answer will arrive
auto channel_name = "response" + std::to_string(xid++);
auto result = Open(channel_name).first;
// register callbacks for the answer
// TODO(zuza): this is actually pretty bad because if SuccessQueryCreateVertex arrives, then
// FailureQueryCreateVertex never gets unsubscribed. This could cause memory leaks
// in the future (not currently since all callbacks get destroyed when channel is closed).
// The best thing to do is to implement a ThenOnce and Either. Perhaps even a ThenClose.
// An Either in conjunction with a failure detector event stream should eventually fail
// the transaction and close the channel.
result->OnEventOnceThenClose<SuccessQueryCreateVertex>(
[this, return_channel](const SuccessQueryCreateVertex &) {
return_channel->Send<ResultMsg>("success");
});
result->OnEventOnceThenClose<FailureQueryCreateVertex>(
[this, return_channel](const FailureQueryCreateVertex &) {
return_channel->Send<ResultMsg>("failure");
});
// instruct worker to make vertex
MakeVertex(wmnid, channel_name);
}
/**
* Asynchronously creates vertex on the give node.
*
* @param mnid Id of the node on which vertex should be created.
* @param channel_name Name of the channel on which response will arrive.
*/
void MakeVertex(MnidT mnid, std::string channel_name) {
MemgraphDistributed::GetInstance().FindChannel(mnid, "worker", "main")
->OnEventOnceThenClose<ChannelResolvedMessage>(
[this, channel_name](const ChannelResolvedMessage &msg){
msg.channelWriter()->Send<QueryCreateVertex>(channel_name);
});
}
protected:
// node id
const MnidT mnid_;
// transaction id
int64_t xid{0};
// list of ids of nodes that act as worker
std::vector<MnidT> worker_mnids_;
};
class Worker : public Reactor {
public:
Worker(std::string name, MnidT mnid)
: Reactor(name), mnid_(mnid), storage_(mnid) {}
virtual void Run() {
Distributed &distributed = Distributed::GetInstance();
std::cout << "Worker (" << mnid_ << ") @ " << distributed.network().Address()
<< ":" << distributed.network().Port() << std::endl;
main_.first->OnEvent<QueryCreateVertex>([this](const QueryCreateVertex& msg,
const Subscription &) {
std::random_device rd; // slow random number generator
// succeed and fail with 50-50 (just for testing)
// TODO: remove random failure
if (rd() % 2 == 0) {
storage_.MakeVertex();
std::cout << "Vertex created" << std::endl;
msg.GetReturnChannelWriter()->Send<SuccessQueryCreateVertex>();
} else {
msg.GetReturnChannelWriter()->Send<FailureQueryCreateVertex>();
}
});
main_.first->OnEvent<QueryVertexCount>([this](const QueryVertexCount &msg,
const Subscription &){
auto count = storage_.VertexCount();
msg.GetReturnChannelWriter()->Send<ResultQueryVertexCount>(count);
});
}
protected:
const MnidT mnid_;
ShardedStorage storage_;
};
int main(int argc, char *argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, /* remove flags from command line */ true);
std::string logging_name = std::string(argv[0]) + "-mnid-" + std::to_string(FLAGS_my_mnid);
google::InitGoogleLogging(logging_name.c_str());
System &system = System::GetInstance();
Distributed& distributed = Distributed::GetInstance();
MemgraphDistributed& memgraph = MemgraphDistributed::GetInstance();
memgraph.RegisterConfig(ParseConfig());
distributed.StartServices();
if (FLAGS_my_mnid == memgraph.LeaderMnid()) {
system.Spawn<Master>("master", FLAGS_my_mnid);
} else {
system.Spawn<Worker>("worker", FLAGS_my_mnid);
}
system.AwaitShutdown();
distributed.StopServices();
return 0;
}

View File

@@ -1,164 +0,0 @@
#pragma once
#include <cassert>
#include <unordered_map>
#include <vector>
#include "uid.hpp"
enum class EdgeType { OUTGOING, INCOMING };
/** A vertex in the graph. Has incoming and outgoing edges which
* are defined as global addresses of other vertices */
class Vertex {
public:
Vertex(const UniqueVid &id) : id_(id) {}
const auto &id() const { return id_; };
const auto &edges_out() const { return edges_out_; }
const auto &edges_in() const { return edges_in_; }
void AddConnection(EdgeType edge_type, const GlobalVertAddress &gad) {
(edge_type == EdgeType::INCOMING ? edges_in_ : edges_out_)
.emplace_back(gad);
}
/** Changes all old_address edges to have the new Memgraph node id */
void RedirectEdges(const GlobalVertAddress& old_address, int64_t new_mnid) {
for (auto &address : edges_in_)
if (address == old_address) address.cur_mnid_ = new_mnid;
for (auto &address : edges_out_)
if (address == old_address) address.cur_mnid_ = new_mnid;
}
private:
UniqueVid id_;
// global addresses of vertices this vertex is connected to
std::vector<GlobalVertAddress> edges_out_;
std::vector<GlobalVertAddress> edges_in_;
};
/**
* A storage that doesn't assume everything is in-memory.
*/
class ShardedStorage {
public:
// Unique Memgraph node ID. Uniqueness is ensured by the (distributed) system.
const int64_t mnid_;
ShardedStorage(int64_t mnid) : mnid_(mnid) {}
int64_t VertexCount() const { return vertices_.size(); }
/** Gets a vertex. */
Vertex &GetVertex(const UniqueVid &gid) {
auto found = vertices_.find(gid);
assert(found != vertices_.end());
return found->second;
}
/**
* Returns the number of edges that cross from this
* node into another one
*/
int64_t BoundaryEdgeCount() const {
int64_t count = 0;
auto count_f = [this, &count](const auto &edges) {
for (const GlobalVertAddress &address : edges)
if (address.cur_mnid_ != mnid_) count++;
};
for (const auto &vertex : vertices_) {
count_f(vertex.second.edges_out());
count_f(vertex.second.edges_in());
}
return count;
}
/** Creates a new vertex on this node. Returns its global id */
const UniqueVid &MakeVertex() {
UniqueVid new_id(mnid_, next_vertex_sequence_++);
auto new_vertex = vertices_.emplace(std::make_pair(new_id, Vertex(new_id)));
return new_vertex.first->first;
};
/** Places the existing vertex on this node */
void PlaceVertex(const UniqueVid &gid, const Vertex &vertex) {
vertices_.emplace(gid, vertex);
}
/** Removes the vertex with the given ID from this node */
void RemoveVertex(const UniqueVid &gid) { vertices_.erase(gid); }
auto begin() const { return vertices_.begin(); }
auto end() const { return vertices_.end(); }
private:
// counter of sequences numbers of vertices created on this node
int64_t next_vertex_sequence_{0};
// vertex storage of this node
std::unordered_map<UniqueVid, Vertex> vertices_;
};
/**
* A distributed system consisting of mulitple nodes.
* For the time being it's not modelling a distributed
* system correctly in terms of message passing (as opposed
* to operating on nodes and their data directly).
*/
class Distributed {
public:
/** Creates a distributed with the given number of nodes */
Distributed(int initial_mnode_count = 0) {
for (int mnode_id = 0; mnode_id < initial_mnode_count; mnode_id++)
AddMnode();
}
int64_t AddMnode() {
int64_t new_mnode_id = mnodes_.size();
mnodes_.emplace_back(new_mnode_id);
return new_mnode_id;
}
int MnodeCount() const { return mnodes_.size(); }
auto &GetMnode(int64_t mnode_id) { return mnodes_[mnode_id]; }
GlobalVertAddress MakeVertex(int64_t mnid) {
return {mnid, mnodes_[mnid].MakeVertex()};
}
Vertex &GetVertex(const GlobalVertAddress &address) {
return mnodes_[address.cur_mnid_].GetVertex(address.uvid_);
}
/** Moves a vertex with the given global id to the given mnode */
void MoveVertex(const GlobalVertAddress &gad, int64_t destination) {
const Vertex &vertex = GetVertex(gad);
// make sure that all edges to and from the vertex are updated
for (auto &edge : vertex.edges_in())
GetVertex(edge).RedirectEdges(gad, destination);
for (auto &edge : vertex.edges_out())
GetVertex(edge).RedirectEdges(gad, destination);
// change vertex destination
mnodes_[destination].PlaceVertex(gad.uvid_, vertex);
mnodes_[gad.cur_mnid_].RemoveVertex(gad.uvid_);
}
void MakeEdge(const GlobalVertAddress &from, const GlobalVertAddress &to) {
GetVertex(from).AddConnection(EdgeType::OUTGOING, to);
GetVertex(to).AddConnection(EdgeType::INCOMING, from);
}
auto begin() const { return mnodes_.begin(); }
auto end() const { return mnodes_.end(); }
private:
std::vector<ShardedStorage> mnodes_;
};

View File

@@ -1,25 +0,0 @@
#include "memgraph_config.hpp"
DEFINE_string(config_filename, "", "File containing list of all processes");
Config ParseConfig(const std::string &filename) {
std::ifstream file(filename, std::ifstream::in);
assert(file.good());
Config config;
while (file.good()) {
MnidT mnid;
std::string address;
uint16_t port;
file >> mnid >> address >> port;
if (file.eof())
break;
config.nodes.push_back(Config::NodeConfig{mnid, address, port});
}
file.close();
return config;
}

View File

@@ -1,39 +0,0 @@
#pragma once
#include <cstdint>
#include <cassert>
#include <fstream>
#include <utility>
#include <vector>
#include <string>
#include <gflags/gflags.h>
/**
* About config file
*
* Each line contains three strings:
* memgraph node id, ip address of the worker, and port of the worker
* Data on the first line is used to start master.
* Data on the remaining lines is used to start workers.
*/
DECLARE_string(config_filename);
using MnidT = uint64_t;
struct Config {
struct NodeConfig {
MnidT mnid;
std::string address;
uint16_t port;
};
std::vector<NodeConfig> nodes;
};
/**
* Parse config file.
*
* @return config object.
*/
Config ParseConfig(const std::string &filename = FLAGS_config_filename);

View File

@@ -1,74 +0,0 @@
#pragma once
#include "memgraph_config.hpp"
#include "reactors_distributed.hpp"
#include <mutex>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
class MemgraphDistributed {
private:
using Location = std::pair<std::string, uint16_t>;
public:
/**
* Get the (singleton) instance of MemgraphDistributed.
*/
static MemgraphDistributed &GetInstance() {
static MemgraphDistributed memgraph;
return memgraph;
}
EventStream *FindChannel(MnidT mnid, const std::string &reactor,
const std::string &channel) {
std::unique_lock<std::mutex> lock(mutex_);
const auto &location = mnodes_.at(mnid);
return Distributed::GetInstance().FindChannel(
location.first, location.second, reactor, channel);
}
void RegisterConfig(const Config &config) {
config_ = config;
for (auto &node : config_.nodes) {
RegisterMemgraphNode(node.mnid, node.address, node.port);
}
}
std::vector<MnidT> GetAllMnids() {
std::vector<MnidT> mnids;
for (auto &node : config_.nodes) {
mnids.push_back(node.mnid);
}
return mnids;
}
/**
* The leader is currently the first node in the config.
*/
MnidT LeaderMnid() const { return config_.nodes.front().mnid; }
protected:
MemgraphDistributed() {}
/** Register memgraph node id to the given location. */
void RegisterMemgraphNode(MnidT mnid, const std::string &address,
uint16_t port) {
std::unique_lock<std::mutex> lock(mutex_);
mnodes_[mnid] = Location(address, port);
}
private:
Config config_;
std::mutex mutex_;
std::unordered_map<MnidT, Location> mnodes_;
MemgraphDistributed(const MemgraphDistributed &) = delete;
MemgraphDistributed(MemgraphDistributed &&) = delete;
MemgraphDistributed &operator=(const MemgraphDistributed &) = delete;
MemgraphDistributed &operator=(MemgraphDistributed &&) = delete;
};

View File

@@ -1,135 +0,0 @@
#pragma once
#include <string>
#include "reactors_local.hpp"
#include "reactors_distributed.hpp"
/**
* Message which encapsulates query.
* It is create on Client and sent to Master which will process it.
*/
class QueryMsg : public ReturnAddressMsg {
public:
QueryMsg(std::string return_channel, std::string query)
: ReturnAddressMsg(return_channel), query_(query) {}
const std::string &query() const { return query_; }
template <class Archive>
void serialize(Archive &archive) {
archive(cereal::virtual_base_class<ReturnAddressMsg>(this), query_);
}
protected:
// Cereal needs access to default constructor.
friend class cereal::access;
QueryMsg() = default;
std::string query_;
};
CEREAL_REGISTER_TYPE(QueryMsg);
/**
* Message which encapuslates result of a query.
* Currently, result is string.
*/
class ResultMsg : public Message {
public:
ResultMsg(std::string result) : result_(result) {}
const std::string &result() const { return result_; }
template <class Archive>
void serialize(Archive &archive) {
archive(cereal::virtual_base_class<Message>(this), result_);
}
protected:
friend class cereal::access;
ResultMsg() = default;
std::string result_;
};
CEREAL_REGISTER_TYPE(ResultMsg);
/**
* Below are message that are exchanged between Master and Workers.
*/
class QueryCreateVertex : public ReturnAddressMsg {
public:
QueryCreateVertex(std::string return_channel)
: ReturnAddressMsg(return_channel) {}
template <class Archive>
void serialize(Archive &archive) {
archive(cereal::virtual_base_class<ReturnAddressMsg>(this));
}
protected:
// Cereal needs access to default constructor.
friend class cereal::access;
QueryCreateVertex() {}
};
CEREAL_REGISTER_TYPE(QueryCreateVertex);
class SuccessQueryCreateVertex : public Message {
public:
SuccessQueryCreateVertex() {}
template <class Archive>
void serialize(Archive &archive) {
archive(cereal::virtual_base_class<Message>(this));
}
};
CEREAL_REGISTER_TYPE(SuccessQueryCreateVertex);
class FailureQueryCreateVertex : public Message {
public:
FailureQueryCreateVertex() {}
template <class Archive>
void serialize(Archive &archive) {
archive(cereal::virtual_base_class<Message>(this));
}
};
CEREAL_REGISTER_TYPE(FailureQueryCreateVertex);
class QueryVertexCount : public ReturnAddressMsg {
public:
QueryVertexCount(std::string return_channel)
: ReturnAddressMsg(return_channel) {}
template <class Archive>
void serialize(Archive &archive) {
archive(cereal::virtual_base_class<ReturnAddressMsg>(this));
}
protected:
// Cereal needs access to default constructor.
friend class cereal::access;
QueryVertexCount() {}
};
CEREAL_REGISTER_TYPE(QueryVertexCount);
class ResultQueryVertexCount : public Message {
public:
ResultQueryVertexCount(int64_t count) : count_(count) {}
int64_t count() const { return count_; }
template <class Archive>
void serialize(Archive &archive) {
archive(cereal::virtual_base_class<Message>(this), count_);
}
protected:
// Cereal needs access to default constructor.
friend class cereal::access;
ResultQueryVertexCount() {}
int64_t count_;
};
CEREAL_REGISTER_TYPE(ResultQueryVertexCount);

View File

@@ -1,147 +0,0 @@
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <experimental/tuple>
#include <iostream>
#include <numeric>
#include <random>
#include <tuple>
#include <vector>
#include "graph.hpp"
namespace spinner {
// const for balancing penalty
double c = 2.0;
/**
* Returns the index of the maximum score in the given vector.
* If there are multiple minimums, one is chosen at random.
*/
auto MaxRandom(const std::vector<double> &scores) {
std::vector<size_t> best_indices;
double current_max = std::numeric_limits<double>::lowest();
for (size_t ind = 0; ind < scores.size(); ind++) {
if (scores[ind] > current_max) {
current_max = scores[ind];
best_indices.clear();
}
if (scores[ind] == current_max) {
best_indices.emplace_back(ind);
}
}
return best_indices[rand() % best_indices.size()];
}
/**
* Returns the index of the best (highest scored) mnode
* for the given vertex. If there are multiple mnodes with
* the best score, vertex prefers to remain on the same mnode
* (if among the best), or one is chosen at random.
*
* @param distributed - the distributed system.
* @param vertex - the vertex which is being evaluated.
* @param penalties - a vector of penalties (per mnode).
* @param current_mnode - the mnode on which the given
* vertex is currently residing.
* @return - std::pair<int, std::vector<double>> which is a
* pair of (best mnode, score_per_mnode).
*/
auto BestMnode(const Distributed &distributed, const Vertex &vertex,
const std::vector<double> &penalties, int current_mnode) {
// scores per mnode
std::vector<double> scores(distributed.MnodeCount(), 0.0);
for (auto &edge : vertex.edges_in()) scores[edge.cur_mnid_] += 1.0;
for (auto &edge : vertex.edges_out()) scores[edge.cur_mnid_] += 1.0;
for (int mnode = 0; mnode < distributed.MnodeCount(); ++mnode) {
// normalize contribution of mnode over neighbourhood size
scores[mnode] /= vertex.edges_out().size() + vertex.edges_in().size();
// add balancing penalty
scores[mnode] -= penalties[mnode];
}
// pick the best destination, but prefer to stay if you can
size_t destination = MaxRandom(scores);
if (scores[current_mnode] == scores[destination])
destination = current_mnode;
return std::make_pair(destination, scores);
}
/** Indication if Spinner mnode penality is calculated based on
* vertex or edge mnode cardinalities */
enum class PenaltyType { Vertex, Edge };
/** Calcualtes Spinner penalties for mnodes in the given
* distributed system. */
auto Penalties(const Distributed &distributed,
PenaltyType penalty_type = PenaltyType::Edge) {
std::vector<double> penalties;
int64_t total_count{0};
for (const auto &mnode : distributed) {
int64_t mnode_count{0};
switch (penalty_type) {
case PenaltyType::Vertex:
mnode_count += mnode.VertexCount();
break;
case PenaltyType::Edge:
for (const auto &vertex_kv : mnode) {
// Spinner counts the edges on a mnode as the sum
// of degrees of vertices on that mnode. In that sense
// both incoming and outgoing edges are individually
// added...
mnode_count += vertex_kv.second.edges_out().size();
mnode_count += vertex_kv.second.edges_in().size();
}
break;
}
total_count += mnode_count;
penalties.emplace_back(mnode_count);
}
for (auto &penalty : penalties)
penalty /= c * total_count / distributed.MnodeCount();
return penalties;
}
/** Do one spinner step (modifying the given distributed) */
void PerformSpinnerStep(Distributed &distributed) {
auto penalties = Penalties(distributed);
// here a strategy can be injected for limiting
// the number of movements performed in one step.
// limiting could be based on (for example):
// - limiting the number of movements per mnode
// - limiting only to movements that are above
// a treshold (score improvement or something)
// - not executing on all the mnodes (also prevents
// oscilations)
//
// in the first implementation just accumulate all
// the movements and execute together.
// relocation info: contains the address of the Vertex
// that needs to relocate and it's destination mnode
std::vector<std::pair<GlobalVertAddress, int>> movements;
for (const ShardedStorage &mnode : distributed)
for (const auto &gid_vertex_pair : mnode) {
// (best destination, scores) pair for vertex
std::pair<int, std::vector<double>> destination_scores =
BestMnode(distributed, gid_vertex_pair.second, penalties, mnode.mnid_);
if (destination_scores.first != mnode.mnid_)
movements.emplace_back(GlobalVertAddress(mnode.mnid_, gid_vertex_pair.first),
destination_scores.first);
}
// execute movements. it is likely that in the real system
// this will need to happen as a single db transaction
for (const auto &m : movements) distributed.MoveVertex(m.first, m.second);
}
} // namespace spinner

View File

@@ -1,45 +0,0 @@
#pragma once
#include <string>
#include <unordered_map>
#include "uid.hpp"
/**
* Mock-up storage to test basic hardcoded queries.
*
* Current code is taken from graph.hpp. It will grow as needed to support
* new queries, but not more. Once all functionality from graph.hpp is
* transfered here, this file will be included in graph.hpp.
*/
class Vertex {};
/**
* Storage that is split over multiple nodes.
*/
class ShardedStorage {
public:
ShardedStorage(int64_t mnid) : mnid_(mnid) {}
/** Returns number of vertices on this node. */
int64_t VertexCount() const { return vertices_.size(); }
/** Creates a new vertex on this node. Returns its global id */
const UniqueVid &MakeVertex() {
UniqueVid new_id(mnid_, next_vertex_sequence_++);
auto new_vertex = vertices_.emplace(std::make_pair(new_id, Vertex()));
return new_vertex.first->first;
};
private:
// unique Memgraph node ID
// uniqueness is ensured by the (distributed) system
const int64_t mnid_;
// counter of vertices created on this node.
int64_t next_vertex_sequence_{0};
// vertex storage of this node
std::unordered_map<UniqueVid, Vertex> vertices_;
};

View File

@@ -1,66 +0,0 @@
#pragma once
#include "utils/hashing/fnv.hpp"
#include <cstdint>
#include <vector>
/**
* Globally unique id (in the entire distributed system) of a vertex.
*
* It is identified by a pair of a (original memgraph node, local vertex id)
*/
class UniqueVid {
public:
UniqueVid(int64_t orig_mnid, int64_t vid)
: orig_mnid_(orig_mnid), vid_(vid) {}
/** Original Memgraph node the vertex was created **/
int64_t orig_mnid_;
/** Original vertex id it was assigned on creation. **/
int64_t vid_;
bool operator==(const UniqueVid &other) const {
return orig_mnid_ == other.orig_mnid_ &&
vid_ == other.vid_;
}
bool operator!=(const UniqueVid &other) const { return !(*this == other); }
};
/**
* Specifies where a vertex is in the distributed system.
*/
class GlobalVertAddress {
public:
GlobalVertAddress(int64_t cur_mnid, const UniqueVid &uvid)
: cur_mnid_(cur_mnid), uvid_(uvid) {}
/** The current Memgraph node where the vertex is **/
int64_t cur_mnid_;
UniqueVid uvid_;
bool operator==(const GlobalVertAddress &other) const {
return cur_mnid_ == other.cur_mnid_ && uvid_ == other.uvid_;
}
bool operator!=(const GlobalVertAddress &other) const {
return !(*this == other);
}
};
namespace std {
template <>
struct hash<UniqueVid> {
size_t operator()(const UniqueVid &uid) const {
return HashCombine<decltype(uid.orig_mnid_), decltype(uid.vid_)>()(uid.orig_mnid_, uid.vid_);
}
};
template <>
struct hash<GlobalVertAddress> {
size_t operator()(const GlobalVertAddress &ga) const {
return HashCombine<decltype(ga.cur_mnid_), decltype(ga.uvid_)>()(ga.cur_mnid_, ga.uvid_);
}
};
}

View File

@@ -1,46 +0,0 @@
#!/usr/bin/env python3
# Automatically copied to the build/ directory during Makefile (configured by cmake)
import os
terminal_command = 'gnome-terminal'
terminal_flags = ' --geometry=200x50 ' # columns x rows
config_filename = 'config'
log_dir = "logs"
glog_flags = '--alsologtostderr --logbufsecs=0 --minloglevel=2 --log_dir="{}" '.format(log_dir)
def GetMainCall(my_mnid, address, port):
ret = "./main {} --my_mnid {} --address {} --port {} --config_filename={}".format(
glog_flags, my_mnid, address, port, config_filename)
print(ret)
return ret
def GetClientCall():
ret = "./main-client {} --address 127.0.0.1 --port 10000 --config_filename={}".format(
glog_flags, config_filename)
print(ret)
return ret
def NamedGnomeTab(name, command):
return " --tab -e \"bash -c 'printf \\\"\\033]0;{}\\007\\\"; {}'\" ".format(name, command)
if __name__ == "__main__":
command = "{} {}".format(terminal_command, terminal_flags)
command += NamedGnomeTab("client", GetClientCall())
f = open(config_filename, 'r')
for line in f:
data = line.strip().split(' ')
my_mnid = data[0]
address = data[1]
port = data[2]
command += NamedGnomeTab("mnid={}".format(my_mnid), GetMainCall(my_mnid, address, port))
print(command)
os.system('mkdir -p {}'.format(log_dir))
os.system(command)

View File

@@ -1,35 +0,0 @@
# set current directory name as a test type
get_filename_component(test_type ${CMAKE_CURRENT_SOURCE_DIR} NAME)
# get all cpp abs file names recursively starting from current directory
file(GLOB_RECURSE test_type_cpps *.cpp)
message(STATUS "Available ${test_type} cpp files are: ${test_type_cpps}")
include_directories(${GTEST_INCLUDE_DIR})
# for each cpp file build binary and register test
foreach(test_cpp ${test_type_cpps})
# get exec name (remove extension from the abs path)
get_filename_component(exec_name ${test_cpp} NAME_WE)
set(target_name memgraph__${test_type}__${exec_name})
# build exec file
add_executable(${target_name} ${test_cpp})
# OUTPUT_NAME sets the real name of a target when it is built and can be
# used to help create two targets of the same name even though CMake
# requires unique logical target names
set_target_properties(${target_name} PROPERTIES OUTPUT_NAME ${exec_name})
# link libraries
target_link_libraries(${target_name} distributed_lib memgraph_lib)
# gtest
target_link_libraries(${target_name} gtest gtest_main)
# register test
set(output_path ${CMAKE_BINARY_DIR}/test_results/unit/${target_name}.xml)
add_test(${target_name} ${exec_name} --gtest_output=xml:${output_path})
endforeach()

View File

@@ -1,3 +0,0 @@
0 127.0.0.1 10000
2 127.0.0.1 10001
3 127.0.0.1 10002

View File

@@ -1,213 +0,0 @@
#include <fstream>
#include <iostream>
#include <glog/logging.h>
#include "memgraph_config.hpp"
#include "reactors_distributed.hpp"
DEFINE_int64(my_mnid, 0, "Memgraph node id"); // TODO(zuza): this should be
// assigned by the leader once in
// the future
class MemgraphDistributed {
private:
using Location = std::pair<std::string, uint16_t>;
public:
/**
* Get the (singleton) instance of MemgraphDistributed.
*
* More info:
* https://stackoverflow.com/questions/1008019/c-singleton-design-pattern
*/
static MemgraphDistributed &GetInstance() {
static MemgraphDistributed
memgraph; // guaranteed to be destroyed, initialized on first use
return memgraph;
}
/** Register memgraph node id to the given location. */
void RegisterMemgraphNode(int64_t mnid, const std::string &address,
uint16_t port) {
std::unique_lock<std::mutex> lock(mutex_);
mnodes_[mnid] = Location(address, port);
}
EventStream *FindChannel(int64_t mnid, const std::string &reactor,
const std::string &channel) {
std::unique_lock<std::mutex> lock(mutex_);
const auto &location = mnodes_.at(mnid);
return Distributed::GetInstance().FindChannel(
location.first, location.second, reactor, channel);
}
protected:
MemgraphDistributed() {}
private:
std::mutex mutex_;
std::unordered_map<int64_t, Location> mnodes_;
MemgraphDistributed(const MemgraphDistributed &) = delete;
MemgraphDistributed(MemgraphDistributed &&) = delete;
MemgraphDistributed &operator=(const MemgraphDistributed &) = delete;
MemgraphDistributed &operator=(MemgraphDistributed &&) = delete;
};
/**
* About config file
*
* Each line contains three strings:
* memgraph node id, ip address of the worker, and port of the worker
* Data on the first line is used to start master.
* Data on the remaining lines is used to start workers.
*/
/**
* Parse config file and register processes into system.
*
* @return Pair (master mnid, list of worker's id).
*/
std::pair<int64_t, std::vector<int64_t>> ParseConfigAndRegister(
const std::string &filename) {
std::ifstream file(filename, std::ifstream::in);
assert(file.good());
int64_t master_mnid;
std::vector<int64_t> worker_mnids;
int64_t mnid;
std::string address;
uint16_t port;
file >> master_mnid >> address >> port;
MemgraphDistributed &memgraph = MemgraphDistributed::GetInstance();
memgraph.RegisterMemgraphNode(master_mnid, address, port);
while (file.good()) {
file >> mnid >> address >> port;
if (file.eof()) break;
memgraph.RegisterMemgraphNode(mnid, address, port);
worker_mnids.push_back(mnid);
}
file.close();
return std::make_pair(master_mnid, worker_mnids);
}
/**
* Sends a text message and has a return address.
*/
class TextMessage : public ReturnAddressMsg {
public:
TextMessage(std::string reactor, std::string channel, std::string s)
: ReturnAddressMsg(reactor, channel), text(s) {}
template <class Archive>
void serialize(Archive &archive) {
archive(cereal::virtual_base_class<ReturnAddressMsg>(this), text);
}
std::string text;
protected:
friend class cereal::access;
TextMessage() {} // Cereal needs access to a default constructor.
};
CEREAL_REGISTER_TYPE(TextMessage);
class Master : public Reactor {
public:
Master(std::string name, int64_t mnid, std::vector<int64_t> &&worker_mnids)
: Reactor(name), mnid_(mnid), worker_mnids_(std::move(worker_mnids)) {}
virtual void Run() {
MemgraphDistributed &memgraph = MemgraphDistributed::GetInstance();
Distributed &distributed = Distributed::GetInstance();
std::cout << "Master (" << mnid_ << ") @ "
<< distributed.network().Address() << ":"
<< distributed.network().Port() << std::endl;
auto stream = main_.first;
// wait until every worker sends a ReturnAddressMsg back, then close
stream->OnEvent<TextMessage>(
[this](const TextMessage &msg, const Subscription &subscription) {
std::cout << "Message from " << msg.Address() << ":" << msg.Port()
<< " .. " << msg.text << "\n";
++workers_seen;
if (workers_seen == static_cast<int64_t>(worker_mnids_.size())) {
subscription.Unsubscribe();
// Sleep for a while so we can read output in the terminal.
// (start_distributed runs each process in a new tab which is
// closed immediately after process has finished)
std::this_thread::sleep_for(std::chrono::seconds(4));
CloseChannel("main");
}
});
// send a TextMessage to each worker
for (auto wmnid : worker_mnids_) {
auto stream = memgraph.FindChannel(wmnid, "worker", "main");
stream->OnEventOnce().ChainOnce<ChannelResolvedMessage>(
[this, stream](const ChannelResolvedMessage &msg,
const Subscription &) {
msg.channelWriter()->Send<TextMessage>("master", "main",
"hi from master");
stream->Close();
});
}
}
protected:
int64_t workers_seen = 0;
const int64_t mnid_;
std::vector<int64_t> worker_mnids_;
};
class Worker : public Reactor {
public:
Worker(std::string name, int64_t mnid, int64_t master_mnid)
: Reactor(name), mnid_(mnid), master_mnid_(master_mnid) {}
virtual void Run() {
Distributed &distributed = Distributed::GetInstance();
std::cout << "Worker (" << mnid_ << ") @ "
<< distributed.network().Address() << ":"
<< distributed.network().Port() << std::endl;
auto stream = main_.first;
// wait until master sends us a TextMessage, then reply back and close
stream->OnEventOnce().ChainOnce<TextMessage>(
[this](const TextMessage &msg, const Subscription &) {
std::cout << "Message from " << msg.Address() << ":" << msg.Port()
<< " .. " << msg.text << "\n";
msg.GetReturnChannelWriter()->Send<TextMessage>("worker", "main",
"hi from worker");
// Sleep for a while so we can read output in the terminal.
std::this_thread::sleep_for(std::chrono::seconds(4));
CloseChannel("main");
});
}
protected:
const int64_t mnid_;
const int64_t master_mnid_;
};
int main(int argc, char *argv[]) {
google::InitGoogleLogging(argv[0]);
gflags::ParseCommandLineFlags(&argc, &argv, true);
System &system = System::GetInstance();
auto mnids = ParseConfigAndRegister(FLAGS_config_filename);
Distributed::GetInstance().StartServices();
if (FLAGS_my_mnid == mnids.first)
system.Spawn<Master>("master", FLAGS_my_mnid, std::move(mnids.second));
else
system.Spawn<Worker>("worker", FLAGS_my_mnid, mnids.first);
system.AwaitShutdown();
Distributed::GetInstance().StopServices();
return 0;
}

View File

@@ -1,67 +0,0 @@
#include <cassert>
#include <iostream>
#include <iterator>
#include "graph.hpp"
void test_global_id() {
UniqueVid a(1, 1);
assert(a == UniqueVid(1, 1));
assert(a != UniqueVid(1, 2));
assert(a != UniqueVid(2, 1));
}
void test_global_address() {
GlobalVertAddress a(1, {1, 1});
assert(a == GlobalVertAddress(1, {1, 1}));
assert(a != GlobalVertAddress(2, {1, 1}));
assert(a != GlobalVertAddress(1, {2, 1}));
}
void test_mnode() {
ShardedStorage mnode0{0};
assert(mnode0.VertexCount() == 0);
UniqueVid n0 = mnode0.MakeVertex();
assert(mnode0.VertexCount() == 1);
ShardedStorage mnode1{1};
mnode1.PlaceVertex(n0, mnode0.GetVertex(n0));
mnode0.RemoveVertex(n0);
assert(mnode0.VertexCount() == 0);
assert(mnode1.VertexCount() == 1);
mnode1.MakeVertex();
assert(mnode1.VertexCount() == 2);
assert(std::distance(mnode1.begin(), mnode1.end()) == 2);
}
void test_distributed() {
Distributed d;
assert(d.MnodeCount() == 0);
auto w0 = d.AddMnode();
assert(d.MnodeCount() == 1);
auto w1 = d.AddMnode();
assert(d.MnodeCount() == 2);
GlobalVertAddress n0 = d.MakeVertex(w0);
assert(d.GetMnode(w0).VertexCount() == 1);
GlobalVertAddress n1 = d.MakeVertex(w1);
assert(d.GetVertex(n0).edges_out().size() == 0);
assert(d.GetVertex(n0).edges_in().size() == 0);
assert(d.GetVertex(n1).edges_out().size() == 0);
assert(d.GetVertex(n1).edges_in().size() == 0);
d.MakeEdge(n0, n1);
assert(d.GetVertex(n0).edges_out().size() == 1);
assert(d.GetVertex(n0).edges_in().size() == 0);
assert(d.GetVertex(n1).edges_out().size() == 0);
assert(d.GetVertex(n1).edges_in().size() == 1);
}
int main() {
test_global_id();
test_global_address();
test_mnode();
test_distributed();
std::cout << "All tests passed" << std::endl;
}

View File

@@ -1,389 +0,0 @@
// This is a deprecated implementation! It is using the deprecated AwaitEvent, I'm changing it to use OnEvent. WIP
// #include <atomic>
// #include <chrono>
// #include <cstdlib>
// #include <iostream>
// #include <string>
// #include <thread>
// #include <vector>
// #include "reactors_distributed.hpp"
// const int NUM_WORKERS = 1;
// class Txn : public ReturnAddressMsg {
// public:
// Txn(std::string reactor, std::string channel, int64_t id) : ReturnAddressMsg(reactor, channel), id_(id) {}
// int64_t id() const { return id_; }
// template <class Archive>
// void serialize(Archive &archive) {
// archive(cereal::base_class<ReturnAddressMsg>(this), id_);
// }
// private:
// int64_t id_;
// };
// class CreateNodeTxn : public Txn {
// public:
// CreateNodeTxn(std::string reactor, std::string channel, int64_t id) : Txn(reactor, channel, id) {}
// template <class Archive>
// void serialize(Archive &archive) {
// archive(cereal::base_class<Txn>(this));
// }
// };
// class CountNodesTxn : public Txn {
// public:
// CountNodesTxn(std::string reactor, std::string channel, int64_t id) : Txn(reactor, channel, id) {}
// template <class Archive>
// void serialize(Archive &archive) {
// archive(cereal::base_class<Txn>(this));
// }
// };
// class CountNodesTxnResult : public Message {
// public:
// CountNodesTxnResult(int64_t count) : count_(count) {}
// int64_t count() const { return count_; }
// template <class Archive>
// void serialize(Archive &archive) {
// archive(count_);
// }
// private:
// int64_t count_;
// };
// class CommitRequest : public ReturnAddressMsg {
// public:
// CommitRequest(std::string reactor, std::string channel, int64_t worker_id)
// : ReturnAddressMsg(reactor, channel), worker_id_(worker_id) {}
// int64_t worker_id() { return worker_id_; }
// template <class Archive>
// void serialize(Archive &archive) {
// archive(cereal::base_class<ReturnAddressMsg>(this), worker_id_);
// }
// private:
// int64_t worker_id_;
// };
// class AbortRequest : public ReturnAddressMsg {
// public:
// AbortRequest(std::string reactor, std::string channel, int64_t worker_id)
// : ReturnAddressMsg(reactor, channel), worker_id_(worker_id) {}
// int64_t worker_id() { return worker_id_; }
// template <class Archive>
// void serialize(Archive &archive) {
// archive(cereal::base_class<ReturnAddressMsg>(this), worker_id_);
// }
// private:
// int64_t worker_id_;
// };
// class CommitDirective : public Message {
// template <class Archive>
// void serialize(Archive &archive) {
// archive(cereal::base_class<Message>(this));
// }
// };
// class AbortDirective : public Message {
// template <class Archive>
// void serialize(Archive &archive) {
// archive(cereal::base_class<Message>(this));
// }
// };
// class Query : public Message {
// public:
// Query(std::string query) : Message(), query_(query) {}
// std::string query() const { return query_; }
// template <class Archive>
// void serialize(Archive &archive) {
// archive(cereal::base_class<Message>(this), query_);
// }
// private:
// std::string query_;
// };
// class Quit : public Message {
// template <class Archive>
// void serialize(Archive &archive) {
// archive(cereal::base_class<Message>(this));
// }
// };
// class Master : public Reactor {
// public:
// Master(System *system, std::string name) : Reactor(system, name), next_xid_(1) {}
// virtual void Run() {
// auto stream = main_.first;
// FindWorkers();
// std::cout << "Master is active" << std::endl;
// while (true) {
// auto m = stream->AwaitEvent();
// if (Query *query = dynamic_cast<Query *>(m.get())) {
// ProcessQuery(query);
// break; // process only the first query
// } else if (ReturnAddressMsg *msg = dynamic_cast<ReturnAddressMsg *>(m.get())) {
// std::cout << "ReturnAddressMsg received!" << std::endl;
// std::cout << " Address: " << msg->Address() << std::endl;
// std::cout << " Port: " << msg->Port() << std::endl;
// std::cout << " Reactor: " << msg->ReactorName() << std::endl;
// std::cout << " Channel: " << msg->ChannelName() << std::endl;
// } else {
// std::cerr << "unknown message\n";
// exit(1);
// }
// }
// stream->OnEvent<Message>([this](const Message &msg, const Subscription& subscription) {
// std::cout << "Processing Query via Callback" << std::endl;
// const Query &query =
// dynamic_cast<const Query &>(msg); // exception bad_cast
// ProcessQuery(&query);
// subscription.Unsubscribe();
// });
// }
// private:
// void ProcessQuery(const Query *query) {
// if (query->query() == "create node") {
// PerformCreateNode();
// } else if (query->query() == "count nodes") {
// PerformCountNodes();
// } else {
// std::cout << "got query: " << query->query() << std::endl;
// }
// }
// void PerformCreateNode() {
// int worker_id = rand() % NUM_WORKERS;
// int64_t xid = GetTransactionId();
// std::string txn_channel_name = GetTxnName(xid);
// auto channel = Open(txn_channel_name);
// auto stream = channel.first;
// channels_[worker_id]->Send<CreateNodeTxn>("master", "main", xid);
// auto m = stream->AwaitEvent();
// if (CommitRequest *req = dynamic_cast<CommitRequest *>(m.get())) {
// req->GetReturnChannelWriter(system_)->Send<CommitDirective>();
// } else if (AbortRequest *req = dynamic_cast<AbortRequest *>(m.get())) {
// req->GetReturnChannelWriter(system_)->Send<AbortDirective>();
// } else {
// std::cerr << "unknown message\n";
// exit(1);
// }
// CloseChannel(txn_channel_name);
// }
// void PerformCountNodes() {
// int64_t xid = GetTransactionId();
// std::string txn_channel_name = GetTxnName(xid);
// auto channel = Open(txn_channel_name);
// auto stream = channel.first;
// for (int w_id = 0; w_id < NUM_WORKERS; ++w_id)
// channels_[w_id]->Send<CountNodesTxn>("master", "main", xid);
// std::vector<std::shared_ptr<Channel>> txn_channels;
// txn_channels.resize(NUM_WORKERS, nullptr);
// bool commit = true;
// for (int responds = 0; responds < NUM_WORKERS; ++responds) {
// auto m = stream->AwaitEvent();
// if (CommitRequest *req = dynamic_cast<CommitRequest *>(m.get())) {
// txn_channels[req->worker_id()] = req->GetReturnChannelWriter(system_);
// commit &= true;
// } else if (AbortRequest *req = dynamic_cast<AbortRequest *>(m.get())) {
// txn_channels[req->worker_id()] = req->GetReturnChannelWriter(system_);
// commit = false;
// } else {
// std::cerr << "unknown message\n";
// exit(1);
// }
// }
// if (commit) {
// for (int w_id = 0; w_id < NUM_WORKERS; ++w_id)
// txn_channels[w_id]->Send<CommitDirective>();
// } else {
// for (int w_id = 0; w_id < NUM_WORKERS; ++w_id)
// txn_channels[w_id]->Send<AbortDirective>();
// }
// int64_t count = 0;
// for (int responds = 0; responds < NUM_WORKERS; ++responds) {
// auto m = stream->AwaitEvent();
// if (CountNodesTxnResult *cnt =
// dynamic_cast<CountNodesTxnResult *>(m.get())) {
// count += cnt->count();
// } else {
// std::cerr << "unknown message\n";
// exit(1);
// }
// }
// CloseChannel(txn_channel_name);
// std::cout << "graph has " << count << " vertices" << std::endl;
// }
// int64_t GetTransactionId() { return next_xid_++; }
// std::string GetWorkerName(int worker_id) {
// return "worker" + std::to_string(worker_id);
// }
// std::string GetTxnName(int txn_id) { return "txn" + std::to_string(txn_id); }
// void FindWorkers() {
// channels_.resize(NUM_WORKERS, nullptr);
// int workers_found = 0;
// while (workers_found < NUM_WORKERS) {
// for (int64_t w_id = 0; w_id < NUM_WORKERS; ++w_id) {
// if (channels_[w_id] == nullptr) {
// // TODO: Resolve worker channel using the network service.
// channels_[w_id] = system_->FindChannel(GetWorkerName(w_id), "main");
// if (channels_[w_id] != nullptr) ++workers_found;
// }
// }
// if (workers_found < NUM_WORKERS)
// std::this_thread::sleep_for(std::chrono::seconds(1));
// }
// }
// // TODO: Why is master atomic, it should be unique?
// std::atomic<int64_t> next_xid_;
// std::vector<std::shared_ptr<Channel>> channels_;
// };
// class Worker : public Reactor {
// public:
// Worker(System *system, std::string name, int64_t id) : Reactor(system, name),
// worker_id_(id) {}
// virtual void Run() {
// std::cout << "worker " << worker_id_ << " is active" << std::endl;
// auto stream = main_.first;
// FindMaster();
// while (true) {
// auto m = stream->AwaitEvent();
// if (Txn *txn = dynamic_cast<Txn *>(m.get())) {
// HandleTransaction(txn);
// } else {
// std::cerr << "unknown message\n";
// exit(1);
// }
// }
// }
// private:
// void HandleTransaction(Txn *txn) {
// if (CreateNodeTxn *create_txn = dynamic_cast<CreateNodeTxn *>(txn)) {
// HandleCreateNode(create_txn);
// } else if (CountNodesTxn *cnt_txn = dynamic_cast<CountNodesTxn *>(txn)) {
// HandleCountNodes(cnt_txn);
// } else {
// std::cerr << "unknown transaction\n";
// exit(1);
// }
// }
// void HandleCreateNode(CreateNodeTxn *txn) {
// auto channel = Open(GetTxnChannelName(txn->id()));
// auto stream = channel.first;
// auto masterChannel = txn->GetReturnChannelWriter(system_);
// // TODO: Do the actual commit.
// masterChannel->Send<CommitRequest>("master", "main", worker_id_);
// auto m = stream->AwaitEvent();
// if (dynamic_cast<CommitDirective *>(m.get())) {
// // TODO: storage_.CreateNode();
// } else if (dynamic_cast<AbortDirective *>(m.get())) {
// // TODO: Rollback.
// } else {
// std::cerr << "unknown message\n";
// exit(1);
// }
// CloseChannel(GetTxnChannelName(txn->id()));
// }
// void HandleCountNodes(CountNodesTxn *txn) {
// auto channel = Open(GetTxnChannelName(txn->id()));
// auto stream = channel.first;
// auto masterChannel = txn->GetReturnChannelWriter(system_);
// // TODO: Fix this hack -- use the storage.
// int num = 123;
// masterChannel->Send<CommitRequest>("master", "main", worker_id_);
// auto m = stream->AwaitEvent();
// if (dynamic_cast<CommitDirective *>(m.get())) {
// masterChannel->Send<CountNodesTxnResult>(num);
// } else if (dynamic_cast<AbortDirective *>(m.get())) {
// // send nothing
// } else {
// std::cerr << "unknown message\n";
// exit(1);
// }
// CloseChannel(GetTxnChannelName(txn->id()));
// }
// // TODO: Don't repeat code from Master.
// std::string GetTxnChannelName(int64_t transaction_id) {
// return "txn" + std::to_string(transaction_id);
// }
// void FindMaster() {
// // TODO: Replace with network service and channel resolution.
// while (!(master_channel_ = system_->FindChannel("master", "main")))
// std::this_thread::sleep_for(std::chrono::seconds(1));
// }
// std::shared_ptr<Channel> master_channel_ = nullptr;
// int worker_id_;
// };
// void ClientMain(System *system) {
// std::shared_ptr<Channel> channel = nullptr;
// // TODO: Replace this with network channel resolution.
// while (!(channel = system->FindChannel("master", "main")))
// std::this_thread::sleep_for(std::chrono::seconds(1));
// std::cout << "I/O Client Main active" << std::endl;
// bool active = true;
// while (active) {
// std::string s;
// std::getline(std::cin, s);
// if (s == "quit") {
// active = false;
// channel->Send<Quit>();
// } else {
// channel->Send<Query>(s);
// }
// }
// }
int main(int, char **) { return 0; }
// int main(int argc, char *argv[]) {
// //google::InitGoogleLogging(argv[0]);
// gflags::ParseCommandLineFlags(&argc, &argv, true);
// System system;
// system.Spawn<Master>("master");
// std::thread client(ClientMain, &system);
// for (int i = 0; i < NUM_WORKERS; ++i)
// system.Spawn<Worker>("worker" + std::to_string(i), i);
// system.AwaitShutdown();
// return 0;
// }

View File

@@ -1,102 +0,0 @@
// command to run:
// gnome-terminal --tab -e './network_chat --port 10000 --minloglevel 2' --tab -e './network_chat --port 10001 --minloglevel 2'
#include "reactors_distributed.hpp"
class ChatMessage : public ReturnAddressMsg {
public:
ChatMessage() : ReturnAddressMsg(), message_("") {}
ChatMessage(std::string reactor, std::string channel, std::string message)
: ReturnAddressMsg(reactor, channel), message_(message) {}
std::string Message() const { return message_; }
template <class Archive>
void serialize(Archive &ar) {
ar(cereal::base_class<ReturnAddressMsg>(this), message_);
}
private:
std::string message_;
};
CEREAL_REGISTER_TYPE(ChatMessage);
class ChatACK : public ChatMessage {
public:
ChatACK() : ChatMessage() {}
ChatACK(std::string reactor, std::string channel, std::string message)
: ChatMessage(reactor, channel, message) {}
template <class Archive>
void serialize(Archive &ar) {
ar(cereal::base_class<ChatMessage>(this));
}
};
CEREAL_REGISTER_TYPE(ChatACK);
class ChatServer : public Reactor {
public:
ChatServer(std::string name)
: Reactor(name) {}
virtual void Run() {
std::cout << "ChatServer is active" << std::endl;
auto chat = Open("chat").first;
chat->OnEvent<ChatACK>([](const ChatACK& ack, const Subscription&) {
std::cout << "Received ACK from " << ack.Address() << ":"
<< ack.Port() << " -> '" << ack.Message() << "'"
<< std::endl;
});
chat->OnEvent<ChatMessage>([this](const ChatMessage& msg, const Subscription&) {
std::cout << "Received message from " << msg.Address() << ":"
<< msg.Port() << " -> '" << msg.Message() << "'"
<< std::endl;
auto channel = msg.GetReturnChannelWriter();
if (channel != nullptr) {
channel->Send<ChatACK>("server", "chat", msg.Message());
}
});
}
};
class ChatClient : public Reactor {
public:
ChatClient(std::string name)
: Reactor(name) {}
virtual void Run() {
std::cout << "ChatClient is active" << std::endl;
std::string address, message;
uint16_t port;
while (true) {
std::cout << "Enter IP, port and message to send." << std::endl;
std::cin >> address >> port >> message;
auto channel =
Distributed::GetInstance().network().Resolve(address, port, "server", "chat");
if (channel != nullptr) {
channel->Send<ChatMessage>("server", "chat", message);
} else {
std::cerr << "Couldn't resolve that server!" << std::endl;
}
}
}
};
int main(int argc, char *argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
System& system = System::GetInstance();
Distributed &distributed = Distributed::GetInstance();
distributed.StartServices();
system.Spawn<ChatServer>("server");
system.Spawn<ChatClient>("client");
system.AwaitShutdown();
distributed.StopServices();
return 0;
}

View File

@@ -1,14 +0,0 @@
#include "reactors_distributed.hpp"
int main(int argc, char *argv[]) {
google::InitGoogleLogging(argv[0]);
Distributed &distributed = Distributed::GetInstance();
distributed.network().StartClient(1);
auto channel = distributed.network().Resolve("127.0.0.1", 10000, "master", "main");
std::cout << channel << std::endl;
if (channel != nullptr) {
channel->Send<ReturnAddressMsg>("master", "main");
}
distributed.network().StopClient();
return 0;
}

View File

@@ -1,85 +0,0 @@
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include "graph.hpp"
#include "spinner.hpp"
void PrintStatistics(const Distributed &distributed) {
using std::cout;
using std::endl;
for (const ShardedStorage &mnode : distributed) {
cout << " ShardedStorage " << mnode.mnid_ << ":";
cout << " #vertices = " << mnode.VertexCount();
int64_t edge_count{0};
for (const auto &gid_vertex_pair : mnode) {
edge_count += gid_vertex_pair.second.edges_in().size();
edge_count += gid_vertex_pair.second.edges_out().size();
}
cout << ", #edges = " << edge_count;
cout << ", #cuts = " << mnode.BoundaryEdgeCount() << endl;
}
}
/**
* Reads an undirected graph from file.
* - first line of the file: vertices_count, edges_count
* - next edges_count lines contain vertices that form an edge
* example:
* https://snap.stanford.edu/data/facebook_combined.txt.gz
* add number of vertices and edges in the first line of that file
*/
Distributed ReadGraph(std::string filename, int mnode_count) {
Distributed distributed(mnode_count);
std::fstream fs;
fs.open(filename, std::fstream::in);
if (fs.fail()) return distributed;
int vertex_count, edge_count;
fs >> vertex_count >> edge_count;
// assign vertices to random mnodes
std::vector<GlobalVertAddress> vertices;
for (int i = 0; i < vertex_count; ++i)
vertices.emplace_back(distributed.MakeVertex(rand() % mnode_count));
// add edges
for (int i = 0; i < edge_count; ++i) {
size_t u, v;
fs >> u >> v;
assert(u < vertices.size() && v < vertices.size());
distributed.MakeEdge(vertices[u], vertices[v]);
}
fs.close();
return distributed;
}
int main(int argc, const char *argv[]) {
srand(time(NULL));
if (argc == 1) {
std::cout << "Usage:" << std::endl;
std::cout << argv[0] << " filename partitions iterations" << std::endl;
return 0;
}
std::cout << "Memgraph spinner test " << std::endl;
std::string filename(argv[1]);
int partitions = std::max(1, atoi(argv[2]));
int iterations = std::max(1, atoi(argv[3]));
Distributed distributed = ReadGraph(filename, partitions);
PrintStatistics(distributed);
for (int iter = 0; iter < iterations; ++iter) {
spinner::PerformSpinnerStep(distributed);
std::cout << "Iteration " << iter << std::endl;
PrintStatistics(distributed);
}
return 0;
}

Some files were not shown because too many files have changed in this diff Show More