Welcome to Objectivity, Inc. -- makers of the industry leading Objectivity/DB object-oriented database management platform, Grid Certified (Levels 1 through 6), and SOA compliant Twitter LinkedIn YouTube RSS Feed

.
Object Oriented Database Learning Center

CONCLUSION

CONCLUSION: We hope that this paper has enabled you to understand a feature of Objectivity/DB that is one of its greatest strengths. What may appear initially as bump in the smooth road is really a stepping stone that enables the software engineer to solve problems more elegantly. We have shown through examples that combining traditional thinking and design methodology with Objectivity’s container architecture results in powerful and unique solutions that you may not have realized were possible. Furthermore, with the flexibility offered only by Objectivity/DB in the aspect of locking granularity versus throughput, you can customize your application in ways impossible with other ODBMs. By taking advantage of the container architecture, high throughput, storage flexibility, security and scalability are much more easily obtained when compared with other architectures.

APPENDIX: Container Pools Pattern Implementation
This section gives a sample implementation of the Container Pools Pattern discussed earlier in this white paper.

Object Creation/Deletion Abstraction: The main area where encapsulation is key is in the area of object creation and deletion. The application developer is used to having the new operator to create objects. We will prevent this by making constructors protected.

The first step is to make the constructors for your persistent-capable classes protected to prevent the application developer from using them directly. Next, you write static create methods for each of these classes which create objects and return handles to them.

Example:

class Data_node : public Graph_node {
public:
   static ooHandle(Data_node)& create(ooHandle(Data_node)&
               inout, Const ooVString& value);
   void send_results();
protected:
   Data_node(ooVstring s):Graph_node(s) {}
private:
   static ContainerPool pool;
};

ooHandle(Data_node)& Data_node:
:create(ooHandle(Data_node)& inout, Const ooVString& value)
{
   inout = new (pool.pickOne(false)) Data_node(value);
   return inout;
}

In the example above, the application developer would call the create method and get back an ooHandle to the new persistent object. Note that the create method as implemented above delegates the responsibility of the physical placement of the object to the pool object which is of type ContainerPool. This object’s sole function is to return a useful container whenever its method pickOne() is called.

The advantage of doing abstraction of the creation and deletion of these persistent objects give you three advantages:

  1. No need to use containers by the application developer
  2. To change the way an object is created/destroyed, you only need to modify the create or destroy method
  3. It is easy to search for code where you are creating or destroying certain objects by searching for classname::create or classname::destroy

A ContainerPool Class

The pool object above is of the ContainerPool class which we will now discuss. This class is available for download from the Objectivity InfoCenter, and can be used as-is, or adapted to suit your own needs. This class is general-purpose enough to permit distribution of objects across multiple hosts, directories and databases. There is a simpler facility for managing pools of containers in a single database, which is part of the d_session class, which is also available on the InfoCenter.

This particular ContainerPool class can be persistent (embedded in another persistent-capable class) or transient. Note that all containers themselves are persistent but the pool object itself can be persistent or transient. A persistent container pool does not have to be reinitialized each time a new process needs it and can be shared among multiple clients. However, if it maintains state information such as how many times a container has been requested from it, or which is the next container to be used, there could be lock contention because you need an update lock on the container pool each time you call member functions which change the state information. If multiple container pools exist which effectively “share” the same real pool of containers, this is perhaps preferable to having to share the single ContainerPool object.

Each ContainerPool instance holds onto an ooVArray(ooRef(ooContObj)) and each call to pickOne() returns an ooRef to one of the container in this array. The actual rules to determine which container is returned can depend on your own pickOne() implementation. For a more intelligent distribution mechanism, you can subclass ContainerPool and add more pickOne() methods which take different kinds of arguments including objects for clustering hints to the ContainerPool.

The ContainerPool class must be initialized once during its lifetime with the init() member function. This member function takes three forms.

  • A reference to an existing container which causes pickOne() to return the same container for all subsequent calls. This is useful for simple single-user benchmarks.
  • A name, number of databases and number of containers per database so that init() can create them.
  • A dbspec object, where you must pass an object that implements the following interface:

    class Dbspec {
       public:
          virtual int numDBs() const = 0;
          virtual const char* host(int I) const = 0;
          virtual const char* path(int I) const = 0;
          virtual const char* sysname(int I) const = 0;
       };

As long as your derived class supports this interface and returns valid hostnames, pathnames and so on, you can implement the class in any number of ways. Using this form of init allows objects to be distributed across multiple hosts, as well as multiple databases. To change the distribution scheme, you only need to modify your own Dbspec-derived class.


Object Oriented Database Learning Center