-
Notifications
You must be signed in to change notification settings - Fork 4
Home
Welcome to the ShmeaDB/GNet wiki!
ShmeaDB is a Function as a Service library.
- We supply many data structures such as GList and GTable.
- We have both a request/response and an event driven actor model.
- We serialize C++ primitives and classes and send them over the wire.
This is our auto-typing class. GTypes are useful for wrapping all types of data. This is the base type of our serialization format.
shmea::GType gInt = 123;
shmea::GType gInt2 = 123;
shmea::GType gInt3 = 93453;
shmea::GType gInt4 = 1;
shmea::GType gFloat = 14.3f;
shmea::GType gFloat2 = 831.3f;
shmea::GType gFloat3 = 1.0f;
shmea::GType gFloat4 = 14.3f;
shmea::GType gDouble = 14.3;
shmea::GType gDouble2 = 654.856;
shmea::GType gDouble3 = 1.43557;
shmea::GType gDouble4 = 1.43557;
shmea::GType gChar = 'a';
shmea::GType gChar2 = '&';
This is an extension of the GType class with extra helper functions specifically for strings. GStrings can be cast as GTypes when pointers.
shmea::GString h = "hello";
shmea::GString w = "world";
shmea::GString hw = h + " " + w;
shmea::GString serverIP = "127.0.0.1";
printf("Date: %s\n", shmea::GString::dateTOstring(time(NULL)).c_str());
A Vector of GTypes.
shmea::GList list0;
list0.addString("derp");
list0.addString("herp");
list0.addString("chirp");
list0.addString("slurp");
list0.addString("burp");
This is a matrix of GTypes. This could also be thought of as a vector of GLists.
shmea::GTable table0;
table0.addRow(list0);
table0.addRow(list1);
A collection of GTables for advanced Serialization.
shmea::GObject Trade::serialize() const
{
shmea::GObject cObj;
shmea::GTable cTable;
shmea::GList cRow;
cRow.addLong(timestamp);
cRow.addInt(actionType);
cRow.addInt(orderType);
cRow.addInt(status);
cRow.addDouble(quantity);
cRow.addDouble(cost);
cTable.addRow(cRow);
cObj.setMembers(cTable);
return cObj;
}
void Trade::deserialize(const shmea::GObject& cObj)
{
const shmea::GTable& cTable = cObj.getMembers();
if((cTable.numberOfRows() == 0) && (cTable.numberOfCols() == 0))
return;
shmea::GList cRow = cTable[0];
timestamp = cRow.getLong(0);
actionType = cRow.getInt(1);
orderType = cRow.getInt(2);
status = cRow.getInt(3);
quantity = cRow.getDouble(4);
cost = cRow.getDouble(5);
}
This is our reference counted smart pointer;
GPointer<GList> p0(new GList());
p0->addString("derp");
p0->addString("herp");
p0->addString("chirp");
p0->addString("slurp");
p0->addString("burp");
printf("Result: %s\n", (p0->size() == 5)? "True":"False";
Output
Result: True
We serialize in order to transport data across the wire as well as save/load data across Gattic instances. We serialize C++ primitives and we provide an interface to serialize classes too. The goal is to standardize our data as much as possible.
// Serialize a list
shmea::GString serializedList0 = shmea::Serializable::Serialize(list0);
shmea::GList deserializedList0;
shmea::Serializable::Deserialize(deserializedList0, serializedList0);
// Serialize a table
shmea::GString serializedStr = shmea::Serializable::Serialize(table0);
shmea::GTable deserializedTable;
shmea::Serializable::Deserialize(deserializedTable, serializedStr);
// Serialize a GObject
serializedStr = shmea::Serializable::Serialize(cObj);
shmea::GObject deserializedObj;
shmea::Serializable::Deserialize(deserializedObj, serializedStr);
// Construct a ServiceData
GNet::Connection* cConnection = NULL;
shmea::ServiceData* cData = new shmea::ServiceData(cConnection, shmea::GString("ServiceNameHere"));
cData->set(cObj);
cData->setArgList(argList);
// Serialize a ServiceData
serializedStr = shmea::Serializable::Serialize(cData);
shmea::ServiceData* deserializedCD = new shmea::ServiceData(cConnection, shmea::GString("ServiceNameHere"));
shmea::Serializable::Deserialize(deserializedCD, serializedStr);
GNet is a service framework that utilizes the actor model. What that means is that we have concurrent boxes with independent states that can communicate with each other.
GNet that allows you to run custom services on the same or separate instances of the platform.
This is a basic example of creating a GServer Instance with a Service:
// Create GServer Instance
GNet::GServer* serverInstance = new GNet::GServer();
// Add services
ML_Train* ml_train_srvc = new ML_Train(serverInstance);
serverInstance->addService(ml_train_srvc);
// Launch the server server
serverInstance->run(isLocalServer);
//BLOCKING CALL HERE
// Cleanup GNet
serverInstance.stop();
// Run a new service
shmea::GList wData;
wData.addString(netName);
wData.addString(testFName);
wData.addInt(importType);
shmea::ServiceData* cSrvc = new shmea::ServiceData(cConnection, "ML_Train");
cSrvc->set(wData);
serverInstance->send(cSrvc);
The argument requirements for a Service Call should be handled in its execute function.
To create a new Service, you must implement a few different functions.
The service's execute function is the method that gets run on when you request a service.
These functions are:
shmea::ServiceData* execute(const shmea::ServiceData* data)
{
class GNet::Connection* destination = data->getConnection();
if (data->getType() != shmea::ServiceData::TYPE_LIST)
return NULL;
shmea::GList cList = data->getList();
if (cList.size() < 1)
return NULL;
shmea::GString netName = cList.getString(0);
shmea::GString testFName = cList.getString(1);
int importType = cList.getInt(2);
int64_t maxTimeStamp = cList.getLong(3);
int64_t maxEpoch = cList.getLong(4);
float maxAccuracy = cList.getFloat(5);
// Do something with the data
...
// Possibility to return another service in response
return NULL;
}
GNet::Service* MakeService(GNet::GServer* newInstance) const
{
return new ML_Train(newInstance);
}
std::string getName() const
{
return "ML_Train";
}
The execute function is the the function that runs every time we hit our actor.
When you launch a service you have to specify a const shmea::ServiceData* data which acts as the request information.
You can also return a shmea::ServiceData* which is the response object that sends at the end of execution of this function. You may leave this empty to do nothing.
The MakeService function contains the constructor that gets called every time you launch a new service.
You can make this any constructor you wish as long as you return a proper NameofNewService*.
The getName() function must return a string representing the unique name of this service for the API.
There are five services GNet provides. You should not use these services as they are internal to GNet.
Class: Bad_Request
Packet: BAD_REQUEST\|
If you ever try call a service that does not exist you execute Bad_Request instead.
Class: Handshake_Client
Packet: HANDSHAKE_CLIENT|name\|
This is a connection request from a new client.
This is the initialization step for a Connection.
Class: Handshake_Server
Packet: HANDSHAKE_SERVER|name|encKey\|
This setups up security keys with the incoming connection.
Class: Logout_Server
Packet: LOGOUT_SERVER\|
This is the connection closing for the server side.
Class: Logout_Client
Packet: LOGOUT_CLIENT\|
This is the connection closing for the client side.
At the highest level on the read side, the Sockets will give you a vector of ServiceData.
At the highest level on the write side, you can send a ServiceData.
ServiceData's are in a string of characters of the format HANDSHAKE_SERVER|name|encKey\| and each byte is encrypted or decrypted accordingly.
You write ServiceData to a service.
Data is returned to you in a vector of ServiceDatas, where each ServiceData is a service to be executed. The ServiceDatas are executed in order of arrival. Each ServiceData is formed by the reader helper functions. TODO: ABOUT READER HELPER FUNCTIONS HERE!!
We use a total of three keys. We use these keys for a custom checksum. The first two keys are random numbers derived from the system timestamp. The third key is an implicit key setup during the initial handshake between client and server.
We use a total of three keys. We use these keys for a custom checksum. The first key is an implicit key setup during the initial handshake between client and server. During the initial connection, this first key is 420. The other two keys are numbers derived from the system timestamp. These two random keys are then used encode the overall security key into a higher dimensionality.
int main(int argc, char* argv[])
{
GNet::GServer* serverInstance = new GNet::GServer();
// Defined elsewhere
MyCustomEvent* newService = new MyCustomEvent(serverInstance);
// Add services to be executed when called from any thread
serverInstance->addService(newService->getName(), newService);
// command line args
bool localOnly = false;
if (argc > 1)
{
localOnly = (strcmp(argv[1], "local") == 0);
}
// Launch the server server
serverInstance->run(localOnly);
// Launch a gui or blocking process
...
// Cleanup GNet
serverInstance->stop();
// Instead of a return or exit necassary
pthread_exit(EXIT_SUCCESS);
}
- You should use
pthread_exit(EXIT_SUCCESS);to exit main() - Try making a service recursively call itself and see how easy it is!