O O D B Design And Implementation - O D M S -

Hi

(cont. of ODBWorker)

I hope you've done something...as Confucius once experienced: learning by doing or as the German used to say "Probieren geht übers Studieren" (Trying is about studying). Let's dig deeper into details about the communication between ODBConnect and ODBWorker. As you've seen in the figure of the previous section ODBConnect starts the communication and stays active as the initiator. ODBWorker is the passive partner who just does what ODBConnect requests: read, update, commit, etc. In order to know what request it is we have to agree on some communication protocol. The Command format could be as following:

Code:
+---------+--------+-----+--------+
| command | dbName | key | Object |
+---------+--------+-----+--------+
Mandatory: command and dbName
Optional:  key and Object

Command:
0: open a connection
1: reserved
2: disconnect
3: getKeys
4: getLocalKey
5: getClusterKey
6: add Object
7: unlock ALL keys
8: update/modify Object
9: delete Object
10: read Object
11: isExisted (a key)
12: isLocked (a key)
13: lock (a key)
14: unlock (a key)
15: rollback (a key)
16: reserved
17: connect (open) to an ODB
18: close (an ODB)
19: save (ODB)
20: UpdateTransaction (13+8+14)
21: DeleteTransaction (13+9+14)
22: commit a key (or a transaction)
23: commit ALL keys (or all transaction)
.
.  undefined. Reserved for future use)
.
96: forcedClose
97: forcedFreeKeys
98: forcedRollback
99: ping a cluster (or node)

The optional parts are depending on the command whether they are needed or not. Example, delete an Object: only command, dbName and key are needed.
On a Distributed (ODB) environment the simplest, but inevitable requirement: the users see only their data, but not where the data are stored. And on the Server site ODBWorker must sometimes look for the source that could be on any node in the cluster. ODBWorker "needs" an army of assistants who work like ODBConnect, but only on behalf of ODBWorker. Inaccessible by Clients (or users). It's the ODBCluster.
PHP:
public class ODBCluster extends ODBConnect
ODBCluster differs from ODBConnect only in 3 functional operations:

  1. No authentication on setup of a communication with other nodes
  2. disconnect won't cause a FreeKeys, Rollback and close activity
  3. NO external Exception

and is created by an Object Data Manager or ODManager and is under its control. They will be only relinquished when ODManager is shutdown (intentionally or unintentionally). Both ODBWorker and ODBCluster are independent threads and run in the common ThreadPool (here: the ForkJoinPool with the WorkStealing functionality).

To fulfill the Client's requests (ODBConnect) ODBWorker who manages all the works for its Client has to interact with the Object Data Management System or ODMS. Meaning that multiple threads to ONE ODMS that deals directly with the cached data and the permanent storage medium. And that requires a lot of synchronization. Otherwise the data consistency cannot be not warranted. The Synchronization Job is done exclusively by ODManager.

There is a lot of synchronization techniques. I show you here 3 possibilities (show = remember) so that you could try to implement (do = understand).

1) Using the keyword "synchronized"
PHP:
    ...
    cluster = new ArrayList<ODBCluster>();
    nodes = new HashMap<String, ODBCluster>();
    kOwner = new HashMap<String, HashMap<String, String>>();
    dbOwner = new HashMap<String, ArrayList<String>>();
    users = new HashMap<String, ArrayList<String>>();
    dbActive = new HashMap<String, Integer>();
    odmsLst = new HashMap<String, ODMS>();
    newLst = new ArrayList<String>();
    dbLst = new ArrayList<String>();
    ...
    public synchronized void update(String uID, String dbName, String key, byte[] obj) throws Exception {
      if (isOwner(uID, dbName) && allKeys(uID, dbName).contains(key)) {
        if (odmsLst.get(dbName).isLocal(key)) {
           odmsLst.get(dbName).modifyObject(key, obj);
          putKey(uID, key);
          return;
        }
        for (ODBCluster cl : cluster) {
          if (cl.isExisted(dbName, key)) {
            cl.update(dbName, key, obj);
            putKey(uID, key);
            return;
          }
        }
      }
      throw new Exception("Unknown "+key+" neither on local or on cluster.");
    }
    ...
2) Using the Wait-Notify facility of JAVA Object
PHP:
    ...
    cluster = new ArrayList<ODBCluster>();
    nodes = new HashMap<String, ODBCluster>();
    kOwner = new HashMap<String, HashMap<String, String>>();
    dbOwner = new HashMap<String, ArrayList<String>>();
    users = new HashMap<String, ArrayList<String>>();
    dbActive = new HashMap<String, Integer>();
    odmsLst = new HashMap<String, ODMS>();
    newLst = new ArrayList<String>();
    dbLst = new ArrayList<String>();
    ...
    public void update(String uID, String dbName, String key, byte[] obj) throws Exception {
      onWait(uID, dbName);
      if (isOwner(uID, dbName) && allKeys(uID, dbName, false).contains(key)) {
        if (odmsLst.get(dbName).isLocal(key)) {
          odmsLst.get(dbName).modifyObject(key, obj);
          putKey(uID, key);
          setFree(dbName);
          return;
        }
        if (uID.charAt(0) != '+') for (ODBCluster odbc : cluster) {
          ArrayList<String> lst = odbc.getLocalKeys(dbName);
          if (lst != null && lst.contains(key)) {
            odbc.update(dbName, key, obj);
            putKey(uID, key);
            setFree(dbName);
            return;
          }
        }
      }
      setFree(dbName);
      throw new Exception("Unknown "+key+" neither on local or on cluster.");
    }
    ...
    // OUR own synchronization
    private synchronized void onWait(String uID, String dbName) {
      if (dbName != null){ // wait for dbName ?
        ArrayList<String> lst = queue.get(dbName);
        lst.add(uID); // insert this userID
        while (!lst.get(0).equals(uID)) try {
          wait(); // wait for being notified
        } catch(InterruptedException ex) { }
      }
    }
    private synchronized void setFree(String dbName) {
      if (dbName != null) {
        ArrayList<String> lst = queue.get(dbName);
        if (lst.size() > 0) lst.remove(0);
        queue.put(dbName, lst);
      }
      notify();
    }
    ..
3) The most modern technique: the JAVA Concurrency.
PHP:
    ...
    kOwner = new ConcurrentHashMap<String, ConcurrentHashMap<String, String>>();
    cluster = Collections.synchronizedList(new ArrayList<ODBCluster>());
    clients = Collections.synchronizedList(new ArrayList<String>());
    newLst = Collections.synchronizedList(new ArrayList<String>());
    dbLst = Collections.synchronizedList(new ArrayList<String>());
    ...
    public void update(String uID, String dbName, String key, byte[] obj) throws Exception {
      if (isOwner(uID, dbName) && allKeys(uID, dbName).contains(key)) {
        if (odmsLst.get(dbName).isLocal(key)) {
          odmsLst.get(dbName).modifyObject(key, obj);
          putKey(uID, dbName, key);
          return;
        }
        if (uID.charAt(0) != '+') {
          String dbname = "+"+uID+"|"+dbName;
          for (ODBCluster odbc : cluster) {
            ArrayList<String> lst = odbc.getLocalKeys(dbname);
            if (lst != null && lst.contains(key)) {
              odbc.update(dbname, key, obj);
              putKey(uID, dbName, key);
              return;
            }
          }
        }
      }
      throw new Exception("Unknown "+key+" neither on local or on cluster.");
    }
    ...
Three possibilities and you may wonder what "technique" is the best, do you? Let me tell you.

  • The first way with the keyword "synchronized" is the simplest and the most dangerous. You could easily run into a deadlock situation and you simply don't know where. In particular when you are working with multiple threading operations. It's simple for a simple case.
  • The second technique is at the first glance very odd. It's odd because most developers don't use it or don't know about it. But it's the most secure way to control the deadlock situations by you yourself. Every operation starts with a onWait() and if there's free it locks it and performs its task and when it finishes the work it sets "it" free (setFree). Free for the (waiting) others.
  • The last option is JAVA concurrency technology. With the Concurrency Package you're pretty sure in safety. However, it could happen with some deadlock situation. But it's usually the problem of your bad logic or your bad algorithm. MORE? You could consult "Concurrent & Parallel Programming". And the Encrypting/Decrypting technique for the UserList: Encrypting/decrypting With Fibionicci Algorithm.

The source of ODBCluster/ODManager API will be bundled and released when this series terminates.
(Next: OODB Design and Implementation: ODMS)
 
Sửa lần cuối:

Bình luận