river-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Gregg Wonderly <gr...@wonderly.org>
Subject Re: datastructure classes
Date Fri, 17 Dec 2010 18:22:56 GMT
On 12/16/2010 9:35 PM, Patricia Shanahan wrote:
> I would love to be able to base it on a ConcurrentMap, but I don't see how it
> would work.
>
> As far as I can tell, there is no clean split between key and non-key among the
> entry's public fields. A template can specify values for any subset of the
> fields, and different templates used in reading from the same space may choose
> different sets of fields.
>
> Any ideas how to work around that?

One of the things I did in Griddle was to create a CHM for each key field name. 
  I then put every entry into the appropriate map based on it having a key value 
pair with a name.

Matching is then a matter of querying each map with the provided key's value, 
and only if the same entry exists in all tables does a match begin processing.

key->map->put(entry,entry).

Thus an entry is keyed based on its uniqueness as an object.  There is not, 
currently, a list of objects, but instead a map so that remove is easy, and does 
not, in general, interfere with reads.

This, ultimately, could result in items not being processed in a high bandwidth 
environment due to how iteratation might work out.  So, I need to do something 
different like use a skip-list or some other queue like mechanism which will 
make sure entries eventually are processed.  A subtle aside, is that since the 
objects are the keys themselves, that to some degree there is an eventual 
completion possible because at some point the "hash" will be ordered to expose 
all entries.

> We can't just use a separate ConcurrentMap for each field, and take the
> intersection of results, because a map requires unique keys. A JavaSpace may
> have many entries with the same value of a given field.

The map described above is about compartmentalization of the entries into "sets" 
that would be most likely to then be matched.  In Outrigger, this might get you 
to a FastList of entries that all expose a value for the same key.  You would 
then need to scan them for the matching value.  From a scale perspective, the 
number of "in transit" objects impacts linear scanning ETA the most.  Heavily 
loaded spaces (ones with slower transaction/transition speeds) will have more 
latency added to the overall throughput of a single operation.

Javaspaces doesn't unmarshal entry data to avoid code downloading.  It demands 
keys be Object types so that a MarshalledInstance can be created in the client 
when the EntryRep is created.  Then, the comparison is about the Entry 
serializing to the same form as expressed in the EntryRep internally.

EntryRep's MarshalledInstance would be exactly the right key to use for further 
compartmentalization to, for example put a list of EntryRep objects with exactly 
the same value.  The simple side issue is that EntryRep doesn't store field 
names.  It only stores values in a array which is ordered based on the order of 
the fields returned from reflection.  So some work would be needed to actually 
have the name of the field in the server.

Let me talk a little bit more about Griddle's TemplateEntry interface shown here:

public interface TemplateEntry<K extends Serializable> extends Serializable {
	// Called with the entry to see if you match.  The called entry will
	// in general be a 'wider' view of the match space than the passed
	// entry.  I.e. ent may have getKeys() return 10 keys, while the
	// called entry may be looking for matches with 3 keys.
	public boolean matches( TemplateEntry<K> ent );
	public List<? extends K> getKeys();
}

This is everything about what Griddle uses for storing and matching.  It leaves 
it up to the implementation to manage the marshal/unmarshal of the entry's 
value.  The matches() method is what is used to see if an entry matches another. 
  When you create a query, you need to decide what getKeys() returns because 
that controls which entries matches() is called with.

So, for example, if you have a basic value object already in your codebase, 
you'd tack on behavior to be a TemplateEntry by adding an interface, but with 
more knowledge of the implementation you'd perhaps not need the interface.

public interface KeyProvider<K,T> {
	public List<K>allKeyNames();
	public Map<K,T> valueMap();
}

public class MyBackingObject implements KeyProvider<String,Object> {
	// This is the object that you want to fly around between clients using
	// the space (griddle)
}

Implementation of the Interface would expose the things that you'd want to be 
considered for matching in the space (griddle).

public class MyTemplateEntry<T extends MyBackingObject>
		 implements TemplateEntry<String> {

	// we marshal the value to carry it to the space.
	private MarshalledObject<T> val;
	// we provide a set of String based key names
	private List<String> keys;
	// We provide the values for those keys as
	// Serializable values.  You do not want to
	// use "random" types for values, only something
	// that the server will have no problem having a
	// class definition for without downloading code, unless
	// that is what you need, and then you just have to
	// think about the impact that has on the server's
	// lifecycle if you need to change the definition
	// of that class.
	private Map<String,Serializable> keyVals;

	public MyTemplateEntry( T value ) {
		val = new MarshalledObject<T>( value );
		// these are separated so that null values
		// can be part of what matches.  Otherwise
		// keys could just be valueMap().keySet().
		keys = value.allKeyNames();
		keyVals = value.valueMap();
	}

	public boolean matches( TemplateEntry<String> ent ) {
		// A more concrete type would not require this
		// check.
		if( ent instanceof MyTemplateEntry == false )
			return false;
		MyTemplateEntry<T extends MyBackingObject> mte =
			(MyTemplateEntry<T extends MyBackingObject>)ent;
		// get the keys for the entry that we are going to see
		// if we match.
		List<String>tkeys = ent.getKeys();
		for( String k : keys ) {
			// we need to find that the passed entry provides
			// a value for the same key.
			boolean found = false;
			for( String tk : tkeys ) {
				if( k.equals(tk) ) {
					// we want to check this value.
					// (need better logic here if we
					// want null == null to work.
					if( keyVals.get(k).equals(
						mte.keyVals.get(k) ) {
						found = true;
					}
				}
			}
			if( !found ) {
				// this entry does not provide a matching
				// value for the key we have.
				return false;
			}
		}
	}
	return true;
}

With all of that in place, a query might then either be a subclass, or it might 
just need to be created using a separate constructor of the base entry class.

public class OwnerMyTemplateEntryQuery extends
		 MyTemplateEntry<KeyProvider<String,Object>> {
	public List<String> getKeys() {
		return Arrays.asList( "first", "last" );
	}
}

The end result I was aiming for, was unlimited matching logic, 
minimization/elimination of downloaded code being possible, and separation of 
keys and the "value" so that anything serializable could be carried around as 
the value, and key/value pairs could be derived or otherwise managed separately.

With outrigger, I think that some of the "not visible" data could be more 
readily included in the EntryRep to make some of the activities of the 
storage/matching easier to do while eliminating some contention by further 
compartmentalization of the data.

Gregg Wonderly

Mime
View raw message