Recovering transactions

Recovery is the process of restoring a federated database to a consistent state after a transaction fails to commit. Depending on the nature of the failure, recovery is performed by the application that started the transaction or through one of several automatic mechanisms that you enable. There is also a manual recovery utility – oocleanup.

Whenever a transaction starts, it records update information in one or more journal files, which are used to return the federated database to its previously committed state if the transaction is aborted or terminated abnormally. Journal files enable Infinite Graph to roll back changes made by incomplete transactions; they differ from relational-database transaction logs, which usually contain before and after images of the data and allow forward recovery up to the last commit.

Journal files (.JNL extension) are written in a federated database’s journal directory. Every single-threaded application that updates the federated database creates a journal file in the journal directory. This journal file is automatically reinitialized at the end of each committed transaction and is reused by the application’s next transaction. A multithreaded application normally creates multiple journal files in the journal directory – one for each thread that executes a separate series of update transactions. As an administrator, you may change a federated database’s journal directory via the ooinstallfd command line tool.

Infinite Graph deletes journal files automatically when a process completes or when recovery is performed after a process or system failure. Never delete a journal file, because data corruption may result.

You can enable lock server to perform automatic recovery of failed transactions. Its recovery monitor is a thread within a standard lock server that identifies and recovers incomplete transactions belonging either to:

  • Processes that ran on client hosts that are now no longer operational.
  • Processes that have been disconnected from the lock server, although their client hosts are still operational.

The recovery monitor checks whether client hosts are operational by attempting to make a network connection to each client host and then waiting for a response within a specified timeout period. If a client host does not respond for any reason (for example, due to machine or network problems), it is assumed to have failed, and all transactions started by its processes are automatically recovered.

The recovery monitor is enabled by starting the lockserver process in the following manner:

oolockserver -monitor

In rare circumstances, Infinite Graph may not be able to recover a federated database automatically. In these situations, you can recover a federated database manually using the oocleanup utility. There are several options that you may specify, however the most common option is the -local flag.

oocleanup -local

The oocleanup tool uses journal files to roll back the uncommitted changes made by incomplete transactions. If the lock server is still running, oocleanup also releases the locks held by the incomplete transactions.

Further information on the oocleanup options can be found here.

File Servers: Advanced Multithreaded Server (AMS)

AMS enables remote applications to access to database files on the computer on which it is running.

When distributing database files over many servers, there are a number of options:

  • NFS (on Unix)
  • Windows Share (Windows mapped drives/ network shares mapped to a drive letter are not supported)
  • AMS (runs on all platforms)

AMS is recommended over NFS and Windows Share for performance and ease of use. Unlike NFS, there is little configuration required for AMS. The AMS is provided with InfiniteGraph and is easy to setup and start.

Port:

The AMS server listens to requests on port 6779. If you are using a firewall, please provide an exception so that port 6779 is open to ensure remote systems can communicate with the AMS.

Command Line Tools

There are three command line tools associated with the AMS. They are listed below. They can be used both on Windows and Unix operating systems:

To start the AMS service:

  • oostartams

To stop the AMS service:

  • oostopams

To check if the AMS is already running:

  • oocheckams

The last two command line tools (oocheckams and oostopams) can be run remotely as well. For example if you want to check to see if an AMS is running on a remote host called myserver.test.com, simply run it with a hostname as an argument:
oocheckams myserver.test.com

Logging and Environment Variables

On Windows, the AMS logs messages to the Application Log which can be viewed with the Windows Event Viewer.

On Unix, the default location for the log is /usr/spool/obj. You can use the OO_SERVER_LOG_DIR environment variable to change this location. For example, to change the log directory to /var/log/infinitegraph in bash:
export OO_SERVER_LOG_DIR=/var/log/infinitegraph

Windows Service

On Windows operating systems, the InfiniteGraph installer will add an AMS service, so that the AMS will start automatically at Windows startup. The name of the service is “ooams-3”.

mrbadforyou

 

Defining Persistence-Capable Classes for InfiniteGraph

A persistence-capable class is one whose instances can be made persistent and saved in an InfiniteGraph database. When you define a persistence-capable class, you must consider its position in the inheritance hierarchies of the application, the range of persistence behavior that the class should support publicly and privately, and which of its fields should be saved persistently. This document discusses the decisions you must make when defining a persistence-capable class and describes how to implement those decisions.

Understanding Persistence-Capable Classes

A persistence-capable class supports InfiniteGraph operations, allowing instances of the class to act both as normal Java runtime objects and as objects stored persistently in a InfiniteGraph federated database. An application that needs to save objects persistently must define a persistence-capable class for each kind of object to be saved.


Note: You should not use the underscore character (_) in the name of any package that contains a persistence-capable class.


When you define a persistence-capable class, you must provide its persistence behavior by inheriting from a persistence capable class (e.g. Inherit from BaseVertex or BaseEdge, or from some subclass of BaseVertex or BaseEdge.) As with any class, defining a persistence-capable class entails defining its fields and methods.

Making a Class Persistence Capable

To provide the capability for persistence you can define a class that inherits from BaseVertex or BaseEdge

Once you have defined a persistence-capable class, any subclass you define from it inherits its persistence behavior.

Example

In this example, Vehicle is a persistence-capable class whose superclass is BaseVertex. Truck is a persistence-capable class whose superclass is Vehicle.

[sourcecode language=”java”]
import com.objy.graph.BaseVertex;

// Make class persistence-capable by inheritance
public class Vehicle extends BaseVertex {

}

[/sourcecode]


[sourcecode language=”java”]
import com.objy.graph.BaseVertex;

// Make class persistence-capable by inheritance
public class Truck extends Vehicle {

}
[/sourcecode]


Defining Fields

Persistent objects can have persistent fields, whose values are saved in the InfiniteGraph federated database, and transient fields, whose values are not saved. Every persistent field represents either an attribute:

  • The attributes of a class constitute its persistent characteristics or state; they enable an application to set the values that define the features or status of each instance of the class.

Persistent Fields

All nonstatic and nonfinal fields you define for a persistence-capable class are persistent by default. When the object is written to the InfiniteGraph federated database, the values in those fields are saved persistently.


Every persistent field must be of one of the following data types.

Category Types
Numeric attributes char

byte

short

int

long

float

double

boolean

String attributes java.lang.String

java.lang.StringBuffer

Date or time attributes java.util.Date

java.sql.Date

java.sql.Time

java.sql.Timestamp

Any field that is not of one of these data types must be declared as transient. See Transient Fields.


Note: If a persistent object has a field for a date, a time, or an array of dates, times, or strings, the value for that field is actually a reference to an internal persistent object representing the data, time, or array data.


If your Java application will interoperate with applications written in C++, C#, or Smalltalk, you must select field types that will map to InfiniteGraph data types that are supported by the other languages. Similarly, if you are defining a Java class corresponding to an existing class description in the schema, you must select field types that will map to the InfiniteGraph attribute and relationship types in the schema description. For more information on this topic, see Objectivity documentation on Schema Matching for Interoperability

Example

This example illustrates the persistent fields that represent attributes

[sourcecode language=”java”]
public class Vehicle extends BaseEdge {
// Attribute fields
protected String license;
protected String type;
protected int doors;
protected int transmission;
protected boolean available;

}
[/sourcecode]


As the preceding table indicates, you should not define persistent fields that reference the database and InfiniteGraph federated database where an object is stored, the transaction to which the object belongs, or the object’s identifier.You can, however, obtain this information through methods defined on BaseVertex or BaseEdge.


Transient Fields

A persistence-capable class can have transient fields, whose values are not saved when an object is written to the InfiniteGraph federated database. To specify that a field is transient, simply give
it the transient modifier when you define your class. Transient fields are not modified when the object’s data is fetched from the InfiniteGraph federated database. Transient field are not copied when a persistent object is copied. You must set the value of a transient field explicitly.

You must define a field as transient in either of the following circumstances:

  • The field’s declared type is not one of the supported types for persistent fields.
  • The field’s declared type is an interface and you intend to reference transient objects in that field.

Example

This example illustrates a transient field dailyRate

[sourcecode language=”java”]
public class Vehicle extends ooObj {

// Transient field
protected transient int dailyRate;

}
[/sourcecode]


Defining Access Methods

When you retrieve a persistent object, you obtain an empty object. You need to fetch the object’s data before you can access the object’s attributes. The methods that fetch data also lock the object if it is not currently locked–for example, because the transaction in which you retrieved the object has committed and therefore released the lock. You can ensure that objects of your class are used safely by accessing attributes and relationships only through access methods that fetch data and obtain locks as necessary.

An additional advantage of using access methods is that they hide the implementation you have chosen, which simplifies the update process if you change your implementation during the prototyping or development phases of your project. For example, suppose you decide to replace a reference attribute with a relationship or vice versa. You would need to reimplement only your access methods; the code that calls the access methods would remain unchanged. This kind of implementation change modifies the class description in the schema. As a consequence, if you change the implementation of your class after you have deployed your application, you need to provide a conversion application to convert objects in the InfiniteGraph federated database from the old implementation to the new implementation. See Objecivity documentation on Schema Evolution and Object Conversion.

If your persistence-capable class extends BaseVertex or BaseEdge, your access methods can call the fetch and markModified methods of the persistent object.

Attribute Access Methods

You should define attribute access methods for every attribute of a class and call those methods whenever you get or set the value of an attribute.

  • It is not safe to access the attributes of a persistent object before the object’s container has been locked for read and its persistent data has been fetched. To ensure that objects of your class are used safely, the access method that gets the value of an attribute should call the fetch method of the object (or its persistor) before returning the attribute’s value.
  • Attributes of an object should be modified only if the object’s container is locked for write and the object’s data has been fetched. If changes are made, the object must be marked as modified so that the changes are written to the federated database. To ensure that your objects are modified safely and that changes are written to the InfiniteGraph federated database, the access method that sets the value of an attribute should call the markModified method of the object (or its persistor) before changing the value.

Note: Any access method that calls the fetch or markModified method of a persistent object must be called within a transaction.
While the object is transient, fetch or markModified have no effect, so they can be called outside a transaction.


Single-Valued Attributes

If an attribute has a single value (as opposed to an array of values), you can define one access method to get the value in the field. If the attribute’s value can be changed, you can define another access method to set the value.

Example

This example illustrates some attribute access methods for the Vehicle class. Each attribute has an access method that gets the value of that attribute; the getLicense method illustrates the form of these methods. All other attributes are initialized when a vehicle object is created; only the available attributes can be modified after initialization. The attribute access methods rentVehicle and returnVehicle set the Boolean attributes available to false and true, respectively.

[sourcecode language=”java”]
// Attribute access methods to get attribute values
public String getLicense() {
fetch();
return this.license;
}

public void rentVehicle() {           // Set available attribute
markModified();
this.available = false;
}

public void returnVehicle() {        // Set available attribute
markModified();
this.available = true;
}
[/sourcecode]


Array Attributes

If your class has array attributes, you can define access methods to get and set the value at a particular array index. If necessary, you can also define an access method to return the array itself.

Example

This example illustrates access methods for an array attribute.
The getInt method gets the value at a particular array index and the setInt method sets the value at a particular index.

[sourcecode language=”java”]
public int getInt(int n) {
fetch();
return this.myIntArray[n];
}

public void setInt(int n, int newInt) {
markModified();
this.myIntArray[n] = newInt;
}
[/sourcecode]

An alternative approach to accessing array fields directly is to hide the implementation with attribute access methods that manage the array.

Defining Application-Required Methods

You can define any methods for your persistence-capable classes that your applications require. Note, however, that InfiniteGraph saves only data persistently, not methods. Thus, if more than one application needs to use persistent objects of a given class, each application must have a definition of that class that
includes both declarations for its attributes and relationships and implementations for its application-defined methods.


Note: If you define a finalize method for a persistence-capable class, you must not perform any InfiniteGraph operation in that method. Different implementations of the Java virtual machine may execute finalize methods in a separate thread. If a finalize method running in its own thread accesses a persistent object whose session enforces the restricted thread policy, a NotJoinedException is thrown.