} // end flush /// <summary> /// Inserts a record into the queue (if all conditions are /// favourable). Returns the queue if it becomes full upon insertion. /// </summary> /// <param name="record">The record to insert into the queue.</param> /// <param name="key">A primary key, if any.</param> /// <returns>The list of records (if the queue is full), or null.</returns> public IList <T> insert(T record, RecordKey key) { if (this.has_primary_key && key.isValid()) { // We are to update the record even if the primary key already exists if (this.update_on_existing_pk) { int key_idx; if (this.primary_key_map.TryGetValue(key, out key_idx)) { // Key exists, so we need to replace the associated record this.queue[key_idx] = record; } else // key does not exist; add the record and { // update the key->record mapping this.queue.Add(record); this.primary_key_map.Add(key, (this.queue.Count - 1)); } } else // do NOT update/add the record if the key already exists { if (this.primary_key_map.ContainsKey(key)) { return(null); // yup, the key already exists } // The key does not exist, so add the record and // update the key->record map this.queue.Add(record); this.primary_key_map.Add(key, (this.queue.Count - 1)); } } else // simply add the record { queue.Add(record); } // If the queue is full, then flush and return the 'old' queue if (queue.Count == capacity) { return(flush()); } else // no records to return { return(null); } } // end insert
} // end private flush() /// <summary> /// Queues a record for insertion into Kinetica. If the queue reaches /// the <member cref="batch_size" />, all records in the queue will be /// inserted into Kinetica before the method returns. If an error occurs /// while inserting the records, the records will no longer be in the queue /// nor in Kinetica; catch <see cref="InsertException{T}"/> to get the list /// of records that were being inserted if needed (for example, to retry). /// </summary> /// <param name="record">The record to insert.</param> /// <exception cref="InsertException{T}" /> public void insert(T record) { // Create the record keys Utils.RecordKey primary_key = null; // used to check for uniqueness Utils.RecordKey shard_key = null; // used to find which worker to send this record to // Build the primary key, if any if (this.primary_key_builder != null) { primary_key = this.primary_key_builder.build(record); } // Build the shard/routing key, if any if (this.shard_key_builder != null) { shard_key = this.shard_key_builder.build(record); } // Find out which worker to send the record to; then add the record // to the approrpriate worker's record queue Utils.WorkerQueue <T> worker_queue; if (this.routing_table == null) { // no information regarding multiple workers, so get the first/only one worker_queue = this.worker_queues[0]; } else if (shard_key == null) { // there is no shard/routing key, so get a random worker worker_queue = this.worker_queues[random.Next(this.worker_queues.Count)]; } else { // Get the worker based on the sharding/routing key int worker_index = shard_key.route(this.routing_table); worker_queue = this.worker_queues[worker_index]; } // Insert the record into the queue IList <T> queue = worker_queue.insert(record, primary_key); // If inserting the queue resulted in flushing the queue, then flush it // properly if (queue != null) { this.flush(queue, worker_queue.url); } } // end insert( record )
} // end constructor RecordKeyBuilder /// <summary> /// Build a RecordKey object based on a record. /// </summary> /// <param name="record">The object based on which the key is to /// be built.</param> /// <returns>The record key that helps is routing this record /// correctly.</returns> public RecordKey build(T record) { // Can't build a key if the buffer size is zero! if (this.buffer_size == 0) { return(null); } // Create the empty key RecordKey key = new RecordKey(this.buffer_size); // Add each routing column's value to the key for (int i = 0; i < this.routing_column_indices.Count; ++i) { // Get the column (with type and name) KineticaType.Column column = this.ktype.getColumns()[this.routing_column_indices[i]]; // Get the value out of the record using the column's name and reflection var value = record.GetType().GetProperty(column.getName()).GetValue(record, null); switch (this.column_types[i]) { case ColumnType.CHAR1: key.addCharN((string)value, 1); break; case ColumnType.CHAR2: key.addCharN((string)value, 2); break; case ColumnType.CHAR4: key.addCharN((string)value, 4); break; case ColumnType.CHAR8: key.addCharN((string)value, 8); break; case ColumnType.CHAR16: key.addCharN((string)value, 16); break; case ColumnType.CHAR32: key.addCharN((string)value, 32); break; case ColumnType.CHAR64: key.addCharN((string)value, 64); break; case ColumnType.CHAR128: key.addCharN((string)value, 128); break; case ColumnType.CHAR256: key.addCharN((string)value, 256); break; case ColumnType.DATE: key.addDate((string)value); break; case ColumnType.DATETIME: key.addDateTime((string)value); break; case ColumnType.DECIMAL: key.addDecimal((string)value); break; case ColumnType.DOUBLE: key.addDouble((double?)value); break; case ColumnType.FLOAT: key.addFloat((float?)value); break; case ColumnType.INT: key.addInt((int?)value); break; case ColumnType.INT8: key.addInt8((int?)value); break; case ColumnType.INT16: key.addInt16((int?)value); break; case ColumnType.IPV4: key.addIPv4((string)value); break; case ColumnType.LONG: key.addLong((long?)value); break; case ColumnType.STRING: key.addString((string)value); break; case ColumnType.TIME: key.addTime((string)value); break; case ColumnType.TIMESTAMP: key.addTimeStamp((long?)value); break; } // end switch } // end for loop // Compute the hash for the key and return it key.computHashes(); return(key); } // end build()
} // end constructor RecordRetriever /// <summary> /// Retrieves records for a given shard key, optionally further limited by an /// additional expression. All records matching the key and satisfying the /// expression will be returned, up to the system-defined limit. For /// multi-head mode the request will be sent directly to the appropriate /// worker. /// <br /> /// All fields in both the shard key and the expression must have defined /// attribute indexes, unless the shard key is also a primary key and all /// referenced fields are in the primary key. The expression must be /// limited to basic equality and inequality comparisons that can be /// evaluated using the attribute indexes. /// </summary> /// /// <param name="record">The record based on whose shard column values /// records will be fetched from the table.</param> /// <param name="expression">An optional expression. Default is /// null.</param> /// /// <returns>A GetRecordsResponse object with the decoded retrieved /// values.</returns> public GetRecordsResponse <T> getRecordsByKey(T record, string expression = null) { if (this.shard_key_builder == null) { throw new KineticaException("Cannot get by key from unsharded table: " + this.table_name); } try { // Build the expression string full_expression = this.shard_key_builder.buildExpression(record); if (full_expression == null) { throw new KineticaException("No expression could be made from given record."); } if (expression != null) { full_expression = (full_expression + " and (" + expression + ")"); } // Create the options map for the /get/records call IDictionary <string, string> options = new Dictionary <string, string>(); options[GetRecordsRequest.Options.EXPRESSION] = full_expression; options[GetRecordsRequest.Options.FAST_INDEX_LOOKUP] = GetRecordsRequest.Options.TRUE; // Create a /get/records request packet GetRecordsRequest request = new GetRecordsRequest(this.table_name, 0, Kinetica.END_OF_SET, options); // Submit the /get/records request if (this.routing_table == null) { // No routing information is available; talk to rank-0 return(kineticaDB.getRecords <T>(request)); } else // Talk to the appropriate worker rank { // Create the appropriate response objects RawGetRecordsResponse raw_response = new RawGetRecordsResponse(); GetRecordsResponse <T> decoded_response = new GetRecordsResponse <T>(); // Find the appropriate worker rank Utils.RecordKey shard_key = this.shard_key_builder.build(record); System.Uri url = this.worker_queues[shard_key.route(this.routing_table)].url; // Make the call raw_response = this.kineticaDB.SubmitRequest <RawGetRecordsResponse>(url, request); // Set up the values of the decoded response properly decoded_response.table_name = raw_response.table_name; decoded_response.type_name = raw_response.type_name; decoded_response.type_schema = raw_response.type_schema; decoded_response.has_more_records = raw_response.has_more_records; decoded_response.total_number_of_records = raw_response.total_number_of_records; // Decode the records kineticaDB.DecodeRawBinaryDataUsingRecordType(ktype, raw_response.records_binary, decoded_response.data); return(decoded_response); } } catch (KineticaException ex) { throw new KineticaException("Error in retrieving records by key: ", ex); } catch (Exception ex) { throw new KineticaException("Error in retrieving records by key: ", ex); } } // end getRecordsByKey()