TinyTable Tabserver example

TinyTable is a SPARKL configuration that acts like a simple database management system. You can use TinyTable to create, lookup and delete records in a database.

Figure: TinyTable Tabserver - The mix


The mix comprises:

  • Four solicit/response operations implemented by the Sequencer service
  • Four request/reply operations implemented by a JavaScript service

As each solicit can trigger a new transaction, four different transactions are possible in this mix.

Each solicit operation is paired up with a request operation.

Each pair forms an island. That is, the vertices generated by the solicit/response operations are connected only with the vertices created by the corresponding request/reply operations.

There are no edges between the islands.

The solicits are used by SPARKL to kick off the transactions.

The JavaScript service uses the requests to create, delete, list or find records in the database.

Tip: Get the code from the SPARKL public repository.
Table 1. TinyTable Tabserver - Markup
Example Description
<folder name="TinyTable">
  <field name="GET"/>
  <field name="INSERT"/>
  <field name="DELETE"/>
  <field name="key" 
    type="integer"/>
  <field name="value" 
    type="string"/>
  <field name="OK"/>
  <field name="error" 
    type="string"/>
  <field name="LIST"/>
  <field name="records" 
    type="string"/>
  <service name="Connection"
    provision="tabserver_connection"/>
  <service name="Sequencer" 
    provision="sequencer"/>
  <service name="Database" 
    provision="tabserver" 
    dependencies="Connection">
    <prop name="tabserver.browser.src"
      src="Database?prop=tabserver.browser.src" 
      type="application/javascript"><![CDATA[
      ...JavaScript comes here...
    ]]></prop>
  </service>
  <mix name="Mix">
    <folder name="Client">
      ...
    </folder>
    <folder name="Server">
    ...
    </folder>
  </mix>
</folder>
Besides the operations, this example comprises:
  • 10 fields sent between the operations
  • The Connection service, which accepts connections from browsers. It is a dependency for the Database service.
  • Database, a service provisioned using the Tabserver extension
  • A piece of JavaScript Database uses to implement the requests. See Figure 2 on the script.
  • Two folders containing the operations

The fields are of different types:

  • FLAGs, used for representing intentions. For example, inserting or deleting a record.
  • Strings, that can carry strings. For example, names.
  • Integers, that can carry integers. For example, a unique ID.

The folders group the operations:

  • Client contains the solicits, which a client service can fire to start transactions.
  • Server contains the requests, which send the fields needed to satisfy the responses.
<solicit name="GetName" 
  service="Sequencer" 
  fields="GET key">
  <response name="Ok" 
    fields="OK value"/>
  <response name="Error" 
    fields="error"/>
</solicit>
...
<request name="Get" 
  service="Database" 
  fields="GET key">
  <reply name="Ok" 
    fields="OK value"/>
  <reply name="Error" 
    fields="error"/>
</request>
The GetName solicit starts a transaction that returns the value of a record based on the key of the record, or sends an error message.

Database uses its JavaScript to implement the Get operation. This operation:

  1. Decides if the value of the field key matches any record.
  2. Depending on the match, it sends either:
    • The Ok reply, with the fields OK and value
    • The Error reply, with the error field
<solicit name="InsertName" 
  service="Sequencer" 
  fields="INSERT value">
  <response name="Ok" 
    fields="OK key"/>
</solicit>
...
<request name="Insert" 
  service="Database" 
  fields="INSERT value">
  <reply name="Ok" 
    fields="OK key"/>
</request>
The InsertName solicit starts a transaction that creates a new record in the database and assigns it a key, which is a unique ID.

Database uses its JavaScript to implement the Insert operation. This operation:

  1. Takes the value received in the value field
  2. Assigns it a unique key
  3. Inserts the new record into the database
  4. Sends the Ok reply with the key field.
<solicit name="DeleteName" 
  service="Sequencer" 
  fields="DELETE key">
  <response name="Ok" 
    fields="OK"/>
  <response name="Error" 
    fields="error"/>
</solicit>
...
<request name="Delete" 
  service="Database" 
  fields="DELETE key">
  <reply name="Ok" 
    fields="OK"/>
  <reply name="Error" 
    fields="error"/>
</request>
The DeleteName solicit starts a transaction that removes an existing record from the database, or sends an error message.

Database uses its JavaScript to implement the Delete operation. This operation:

  1. Decides if the value of the field key matches any record.
  2. Depending on the match, it sends either:
    • The Ok reply, with the field OK
    • The Error reply, with the error field
<solicit name="ListNames" 
  service="Sequencer" 
  fields="LIST">
  <response name="Ok" 
    fields="records"/>
</solicit>
...
<request name="List" 
  service="Database" 
  fields="LIST">
  <reply name="Ok" 
    fields="records"/>
</request>
The ListNames solicit starts a transaction that retrieves all records from the database.

Database uses its JavaScript to implement the List operation.

This operation sends all records of the database as a string.

Figure: TinyTable - JavaScript content of Database

(function(sparkl) {
  var table = [];

  console.log("Loaded memory-based table implementation.");

  /**
   * Server implementation has a function per operation.
   */
  sparkl.service({

    onInit : function() {
      console.log("Memory-based table ready.");
    },

    onClose : function() {
      console.error("Goodbye cruel world.");
    },

    "Insert" : function(request, reply) {
      var key = table.length;
      table[key] = request.value("value");
      console.log("Inserted record #" + key);
      reply["Ok"]
        .value("key", key)
        .send();
    },
    
    "List" : function(request, reply) {
      var records = table.toString();
      reply["Ok"]
      .value("records", records)
      .send();
    },

    "Delete" : function(request, reply) {
      var key = request.value("key");
      if (typeof table[key] != "undefined") {
        delete table[key];
        console.log("Deleted record #" + key);
        reply["Ok"]
          .send();
      }
      else {
        console.error("Cannot delete. No such key #" + key);
        reply["Error"]
          .value("error", "Not found")
          .send()
      }
    },

    "Get" : function(request, reply) {
      var key = request.value("key");
      if (typeof table[key] != "undefined") {
        console.log("Retrieved record #" + key);
        reply["Ok"]
          .value("value", table[key])
          .send();
      }
      else {
        console.error("Cannot get. No such key #" + key);
        reply["Error"]
          .value("error", "Not found")
          .send();
      };
    }
  });

})(sparkl);