/** * */ package smartkv.client.workloads; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import smartkv.client.AbstractDatastoreProxy; import smartkv.client.ColumnProxy; import smartkv.client.KeyValueProxy; import smartkv.client.tables.AnnotatedColumnObject; import smartkv.client.tables.ColumnObject; import smartkv.client.tables.ColumnTable; import smartkv.client.tables.ColumnTable_; import smartkv.client.tables.KeyValueTable; import smartkv.client.tables.KeyValueTable_; import smartkv.client.util.Serializer; import smartkv.client.util.UnsafeJavaSerializer; import smartkv.server.RequestType; //TODO - It would be nice to have an implementation of this that did not require using the smart middleware, only the server implementation locally. It would be faster to perform Workload analysis. /** * A Data store proxy client that records <b>RPC</b> requests characteristics for <b>one</b> request. * Yeah, I known, one instance per/ request sounds lame. I didn't find a way to make this more efficient. However it does the job, and usually while recording * requests characteristics we do not care about efficiency. At least in the use for which this class was designed for we don't. * <p> * * Each instance of this class must keep a reference to a {@link RequestLogEntry }. For each request invocation * this RequestLogEntry will be set up with: * <ul> * <li> {@link RequestLogEntry#getTimeStarted() timeStarted} the time (ns) at which the request was sent to the data store. </li> * <li> {@link RequestLogEntry#getTimeEnded() timeEnded } the time (ns) at which a reply was received from the data store. </li> * <li> {@link RequestLogEntry#getSizeOfRequest() sizeOfRequest} the size (bytes) of the byte array payload sent to the data store. </li> * <li> {@link RequestLogEntry#getSizeOfResponse() sizeOfResponse} the size (bytes) of the reply payload sent from the data store </li> * <li> {@link RequestLogEntry#getType() type} the operation performed on the data store</li> * <li> </li> * </ul> * * The values of <code>timeStarted</code> and TimeEnded are taken before and after the invocation of {@link AbstractDatastoreProxy#invokeRequest(RequestType, byte[])}. There units are taken in nanoseconds. * <p> * As for <code>sizeOfRequest</code> and <code>sizeOfResponse</code> they measure in bytes the request and reply <b>RPC</b> payload sent/received from the data store. * Please notice that this does not take into account: the total packet size sent, or the overhead of the data store middleware protocol in use (e.g., consensus data). * <p> * Finally, <code>type</code> sets the <b>RPC</b> operation information ({@link RequestType}). * <p> * */ /*class WorkloadLoggerDataStoreProxy { private RequestLogEntry logEntry; *//** * Create *//* public WorkloadLoggerDataStoreProxy(RequestLogEntry logEntry){ super(0); //FIXME this.logEntry = logEntry; } *//** * Records requests characteristics in a {@link RequestLogEntry}. * <p> * Characteristics recorded are: timeStarted/timeEnded (ns); sizeOfRequest/sizeofResponse (bytes) and type (RequestType). * * @see bonafide.datastore.AbstractDatastoreProxy#invokeRequest(mapserver.RequestType, byte[]) * @see WorkloadLoggerDataStoreProxy *//* @Override protected byte[] invokeRequest(RequestType type, byte[] request) { logEntry.setTimeStarted(System.nanoTime()); logEntry.setSizeOfRequest(request.length); logEntry.setType(type); byte[] result = super.invokeRequest(type, request); logEntry.setTimeEnded(System.nanoTime()); return result; } *//** * Returns the RequestLogEntry used to capture the request characteristics. * @return the RequestLogEntry used. *//* public RequestLogEntry getLogEntry(){ return logEntry; } } */ /** * @author fabiim * */ /* * In a nutshell. Each request to the datastore will be logged. * The RequestLogEntry entry is always the same reference. The underlying WorkloadLoggerDataStoreProxy will change it everytime * a request is made to the datastore. (this is actually not thread safe, so we synchronize every request to this facade Table). * This is not meant to be used when performance is the issue. */ public class WorkloadLoggerTable<K,V> implements KeyValueTable<K,V>{ KeyValueTable<K,V> table; RequestLogEntry entry; String tableName; RequestLogger logger; /** * Construct a TableLogger with default (unsafe) serializers for keys and values. */ public WorkloadLoggerTable(String tableName, RequestLogger logger){ this(tableName, logger, UnsafeJavaSerializer.<K>getInstance(), UnsafeJavaSerializer.<V>getInstance()); } protected WorkloadLoggerTable(){ } public WorkloadLoggerTable(String tableName, RequestLogger logger, Serializer<K> keys, Serializer<V> values){ this(tableName, logger, keys, values, null, null); } public WorkloadLoggerTable(String tableName, RequestLogger logger, Serializer<K> keys, Serializer<V> values, String tablereference, Serializer<Object> referenceSerializer){ this.entry = new RequestLogEntry(); this.logger = logger; this.tableName = tableName; this.table = KeyValueTable_.getTable( new KeyValueProxy(0){ @Override protected byte[] invokeRequestWithRawReturn(RequestType type, byte[] request) { entry.setTimeStarted(System.currentTimeMillis()); entry.setSizeOfRequest(request.length); entry.setType(type); byte[] result = invokeRequestWithRawReturn(type, request); entry.setTimeEnded(System.currentTimeMillis()); return result; }; } , tableName, keys,values, tablereference, referenceSerializer); } @Override public synchronized boolean remove(K key, V value) { boolean val = table.remove(key, value); logEntry(new RequestLogWithDataInformation.Builder().setTable(tableName). setKey(key != null? key.toString() : "null" ).setValue(value != null ? value.toString() : "null").build(entry)); return val; } /** * @param build */ protected void logEntry(RequestLogWithDataInformation req) { req.setStrackTrace(); logger.addRequest(req); } @Override public synchronized V putIfAbsent(K key, V value) { V val = table.putIfAbsent(key, value); logEntry(new RequestLogWithDataInformation.Builder(). setTable(tableName). setKey(key != null? key.toString() : "null" ). setValue(value != null ? value.toString() : "null"). setReturnedValue(val != null ? val.toString() : "null" ). build(entry)); return val; } @Override public synchronized void clear() { table.clear(); logEntry(new RequestLogWithDataInformation.Builder(). setTable(tableName). build(entry)); } @Override public synchronized boolean containsKey(K key) { Boolean val = table.containsKey(key); logEntry(new RequestLogWithDataInformation.Builder(). setTable(tableName). setKey(key != null? key.toString() : "null" ). setReturnedValue(val != null ? val.toString() : "null" ). build(entry)); return val; } @Override public synchronized V remove(K key) { V val = table.remove(key); logEntry(new RequestLogWithDataInformation.Builder(). setTable(tableName). setKey(key != null? key.toString() : "null" ). setReturnedValue(val != null ? val.toString() : "null" ). build(entry)); return val; } @Override public synchronized Set<Entry<K, V>> entrySet() { Set<Entry<K,V>> entries = table.entrySet(); logEntry(new RequestLogWithDataInformation.Builder(). setTable(tableName). setReturnedValue(entries.toString()). build(entry)); return entries; } @Override public synchronized V put(K key, V value) { V val = table.put(key, value); logEntry(new RequestLogWithDataInformation.Builder(). setTable(tableName). setKey(key != null? key.toString() : "null" ). setValue(value != null ? value.toString() : "null"). setReturnedValue(val != null ? val.toString() : "null" ). build(entry)); return val; } @Override public synchronized boolean isEmpty() { Boolean val = table.isEmpty(); logEntry(new RequestLogWithDataInformation.Builder(). setTable(tableName). build(entry)); return val; } @Override public synchronized Set<K> keySet() { Set<K> keys = table.keySet(); logEntry(new RequestLogWithDataInformation.Builder(). setTable(tableName). setReturnedValue(keys.toString()). build(entry)); return keys; } @Override public synchronized V get(K key) { V val = table.get(key); logEntry(new RequestLogWithDataInformation.Builder(). setTable(tableName). setKey(key != null? key.toString() : "null" ). setReturnedValue(val != null ? val.toString() : "null" ). build(entry)); return val; } @Override public synchronized void putAll(Map<? extends K, ? extends V> m) { table.putAll(m); logEntry(new RequestLogWithDataInformation.Builder(). setTable(tableName). setKey(m != null? m.toString() : "null" ). build(entry)); } @Override public synchronized boolean insert(K key, V value) { Boolean b = table.insert(key, value); logEntry(new RequestLogWithDataInformation.Builder(). setTable(tableName). setKey(key != null? key.toString() : "null" ). setValue(value != null ? value.toString() : "null"). setReturnedValue(b != null? b.toString(): "null"). build(entry)); return b; } @Override public synchronized int size() { Integer val = table.size(); logEntry(new RequestLogWithDataInformation.Builder(). setTable(tableName). setReturnedValue(val != null ? val.toString() : "null" ). build(entry)); return val; } @Override public synchronized Collection<V> values() { Collection<V> values = table.values(); logEntry(new RequestLogWithDataInformation.Builder(). setTable(tableName). setReturnedValue(values != null ? values.toString(): "null"). build(entry)); return values; } @Override public synchronized int getAndIncrement(String key) { Integer val = table.getAndIncrement(key); logEntry(new RequestLogWithDataInformation.Builder(). setTable(tableName). setKey(key != null? key.toString() : "null" ). setReturnedValue(val != null ? val.toString() : "null" ).build(entry)); return val; } /* (non-Javadoc) * @see bonafid * * e.datastore.tables.KeyValueTable#getValueByReference(java.lang.Object) */ @Override public <V1> V1 getValueByReference(K key) { V1 val = (V1) table.getValueByReference(key); logEntry(new RequestLogWithDataInformation.Builder(). setTable(tableName). setKey(key != null? key.toString() : "null" ). setReturnedValue(val != null ? val.toString() : "null" ). build(entry)); return val; } /* (non-Javadoc) * @see bonafide.datastore.tables.KeyValueTable#replace(java.lang.Object, java.lang.Object, java.lang.Object) */ @Override public boolean replace(K key, V currentValue, V newValue) { Boolean val = table.replace(key, currentValue, newValue); logEntry(new RequestLogWithDataInformation.Builder(). setTable(tableName). setKey(key != null ? key.toString() : "null"). setExistentValue(currentValue != null ? currentValue.toString() : "null"). setValue(newValue!= null ?newValue.toString() : "null"). setReturnedValue(val.toString()).build(entry)); return val; } }