/** * Forcefully shuts down the connection to this tablet server and fails all the outstanding RPCs. * Only use when shutting down a client. * * @return deferred object to use to track the shutting down of this connection */ public Deferred<Void> shutdown() { // First, check whether we have RPCs in flight and cancel them. for (Iterator<KuduRpc<?>> ite = rpcs_inflight.values().iterator(); ite.hasNext(); ) { KuduRpc<?> rpc = ite.next(); rpc.errback(new ConnectionResetException(null)); ite.remove(); } // Same for the pending RPCs. synchronized (this) { if (pending_rpcs != null) { for (Iterator<KuduRpc<?>> ite = pending_rpcs.iterator(); ite.hasNext(); ) { ite.next().errback(new ConnectionResetException(null)); ite.remove(); } } } final Channel chancopy = chan; if (chancopy == null) { return Deferred.fromResult(null); } if (chancopy.isConnected()) { Channels.disconnect(chancopy); // ... this is going to set it to null. // At this point, all in-flight RPCs are going to be failed. } if (chancopy.isBound()) { Channels.unbind(chancopy); } // It's OK to call close() on a Channel if it's already closed. final ChannelFuture future = Channels.close(chancopy); // Now wrap the ChannelFuture in a Deferred. final Deferred<Void> d = new Deferred<Void>(); // Opportunistically check if it's already completed successfully. if (future.isSuccess()) { d.callback(null); } else { // If we get here, either the future failed (yeah, that sounds weird) // or the future hasn't completed yet (heh). future.addListener( new ChannelFutureListener() { public void operationComplete(final ChannelFuture future) { if (future.isSuccess()) { d.callback(null); return; } final Throwable t = future.getCause(); if (t instanceof Exception) { d.callback(t); } else { // Wrap the Throwable because Deferred doesn't handle Throwables, // it only uses Exception. d.callback( new NonRecoverableException("Failed to shutdown: " + TabletClient.this, t)); } } }); } return d; }
@Test(dependsOnGroups = "example.ipc.server") public void clientRPC() throws Exception { RPC.ClientRPC client = RPC.getClientRPC(socketAddress); GetRequest request = GetRequest.newBuilder().setRow(ByteString.copyFromUtf8("row1")).build(); Deferred<GetResponse> response = client.getData(request); GetResponse data = response.join(); System.out.println(data); Deferred<GetResponse> deferred = client.getData(request); deferred .addCallback( new Callback<Object, GetResponse>() { public Object call(final GetResponse getResponse) throws Exception { System.out.println(getResponse); return null; } }) .addErrback( new Callback<Object, Object>() { public Object call(final Object o) throws Exception { System.err.println("ERROR: " + o); return null; } }); }
protected static KuduTable createTable( String tableName, Schema schema, CreateTableBuilder builder) { LOG.info("Creating table: {}", tableName); Deferred<KuduTable> d = client.createTable(tableName, schema, builder); final AtomicBoolean gotError = new AtomicBoolean(false); d.addErrback( new Callback<Object, Object>() { @Override public Object call(Object arg) throws Exception { gotError.set(true); LOG.error("Error : " + arg); return null; } }); KuduTable table = null; try { table = d.join(DEFAULT_SLEEP); } catch (Exception e) { fail("Timed out"); } if (gotError.get()) { fail( "Got error during table creation, is the Kudu master running at " + masterAddresses + "?"); } tableNames.add(tableName); return table; }
@Override public Deferred<TreeRule> call(final ArrayList<KeyValue> row) { if (row == null || row.isEmpty()) { return Deferred.fromResult(null); } return Deferred.fromResult(parseFromStorage(row.get(0))); }
/** * Wait up to DEFAULT_SLEEP for an expected count of TS to connect to the master * * @param expected How many TS are expected * @return true if there are at least as many TS as expected, otherwise false */ static boolean waitForTabletServers(int expected) throws Exception { int count = 0; Stopwatch stopwatch = new Stopwatch().start(); while (count < expected && stopwatch.elapsedMillis() < DEFAULT_SLEEP) { Thread.sleep(200); Deferred<ListTabletServersResponse> d = client.listTabletServers(); d.addErrback(defaultErrorCB); count = d.join(DEFAULT_SLEEP).getTabletServersCount(); } return count >= expected; }
public Deferred<HttpResponse> process(ChannelHandlerContext context, HttpRequest request) { HttpResponse response = new DefaultHttpResponse(request.getProtocolVersion(), HttpResponseStatus.OK); response.setHeader("content-type", "text/html"); response.setContent( ChannelBuffers.copiedBuffer("Accepted ShutDown Request", Charset.defaultCharset())); Deferred<HttpResponse> deferred = new Deferred<HttpResponse>(); deferred.callback(response); shutdown(); super.doShutdown(context.getChannel()); return deferred; }
/** * Find the port of the leader master in order to retrieve it from the port to process map. * * @return The port of the leader master. * @throws Exception If we are unable to find the leader master. */ protected static int findLeaderMasterPort() throws Exception { Stopwatch sw = new Stopwatch().start(); int leaderPort = -1; while (leaderPort == -1 && sw.elapsedMillis() < DEFAULT_SLEEP) { Deferred<Master.GetTableLocationsResponsePB> masterLocD = client.getMasterTableLocationsPB(); Master.GetTableLocationsResponsePB r = masterLocD.join(DEFAULT_SLEEP); leaderPort = r.getTabletLocations(0).getReplicas(0).getTsInfo().getRpcAddresses(0).getPort(); } if (leaderPort == -1) { fail("No leader master found after " + DEFAULT_SLEEP + " ms."); } return leaderPort; }
/** Helper to mock the UID caches with valid responses */ private void setGetUidName() { when(metrics.getNameAsync(new byte[] {0, 0, 1})).thenReturn(Deferred.fromResult("sys.cpu.0")); when(metrics.getNameAsync(new byte[] {0, 0, 2})) .thenThrow(new NoSuchUniqueId("metric", new byte[] {0, 0, 2})); when(tag_names.getNameAsync(new byte[] {0, 0, 1})).thenReturn(Deferred.fromResult("host")); when(tag_names.getNameAsync(new byte[] {0, 0, 2})) .thenThrow(new NoSuchUniqueId("tagk", new byte[] {0, 0, 2})); when(tag_values.getNameAsync(new byte[] {0, 0, 1})).thenReturn(Deferred.fromResult("web01")); when(tag_values.getNameAsync(new byte[] {0, 0, 2})) .thenThrow(new NoSuchUniqueId("tag_values", new byte[] {0, 0, 2})); }
/** * Blocking call with a different behavior based on the flush mode. PleaseThrottleException is * managed by this method and will not be thrown, unlike {@link AsyncKuduSession#apply}. * * <p> * * <ul> * <li>AUTO_FLUSH_SYNC: the call returns when the operation is persisted, else it throws an * exception. * <li>AUTO_FLUSH_BACKGROUND: the call returns when the operation has been added to the buffer. * This call should normally perform only fast in-memory operations but it may have to wait * when the buffer is full and there's another buffer being flushed. Row errors can be * checked by calling {@link #countPendingErrors()} and can be retrieved by calling {@link * #getPendingErrors()}. * <li>MANUAL_FLUSH: the call returns when the operation has been added to the buffer, else it * throws an exception such as a NonRecoverableException if the buffer is full. * </ul> * * @param operation operation to apply * @return an OperationResponse for the applied Operation * @throws Exception if anything went wrong */ public OperationResponse apply(Operation operation) throws Exception { while (true) { try { Deferred<OperationResponse> d = session.apply(operation); if (getFlushMode() == FlushMode.AUTO_FLUSH_SYNC) { return d.join(getTimeoutMillis()); } break; } catch (PleaseThrottleException ex) { try { ex.getDeferred().join(getTimeoutMillis()); } catch (Exception e) { // This is the error response from the buffer that was flushing, // we can't do much with it at this point. LOG.error("Previous batch had this exception", e); } } catch (Exception e) { throw e; } } return null; }
@Override public Deferred<Object> call(final ArrayList<KeyValue> row) throws Exception { if (row == null || row.isEmpty()) { return Deferred.fromResult(null); } final ArrayList<byte[]> qualifiers = new ArrayList<byte[]>(row.size()); for (KeyValue column : row) { if (column.qualifier().length > RULE_PREFIX.length && Bytes.memcmp(RULE_PREFIX, column.qualifier(), 0, RULE_PREFIX.length) == 0) { qualifiers.add(column.qualifier()); } } final DeleteRequest delete = new DeleteRequest( tsdb.treeTable(), Tree.idToBytes(tree_id), Tree.TREE_FAMILY(), qualifiers.toArray(new byte[qualifiers.size()][])); return tsdb.getClient().delete(delete); }
/** * Counts the rows from the {@code scanner} until exhaustion. It doesn't require the scanner to be * new, so it can be used to finish scanning a previously-started scan. */ protected static int countRowsInScan(AsyncKuduScanner scanner) throws Exception { final AtomicInteger counter = new AtomicInteger(); Callback<Object, RowResultIterator> cb = new Callback<Object, RowResultIterator>() { @Override public Object call(RowResultIterator arg) throws Exception { if (arg == null) return null; counter.addAndGet(arg.getNumRows()); return null; } }; while (scanner.hasMoreRows()) { Deferred<RowResultIterator> data = scanner.nextRows(); data.addCallbacks(cb, defaultErrorCB); data.join(DEFAULT_SLEEP); } Deferred<RowResultIterator> closer = scanner.close(); closer.addCallbacks(cb, defaultErrorCB); closer.join(DEFAULT_SLEEP); return counter.get(); }
/** Method to call after writing the HTTP response to the wire. */ private void done() { final int processing_time = processingTimeMillis(); httplatency.add(processing_time); logInfo("HTTP " + getUri() + " done in " + processing_time + "ms"); deferred.callback(null); }
@Override public Deferred<Object> shutdown() { return Deferred.fromResult(new Object()); }
/** * Helper method to open a table. It sets the default sleep time when joining on the Deferred. * * @param name Name of the table * @return A KuduTable * @throws Exception MasterErrorException if the table doesn't exist */ protected static KuduTable openTable(String name) throws Exception { Deferred<KuduTable> d = client.openTable(name); return d.join(DEFAULT_SLEEP); }
/** * Handles CRUD calls to individual TSMeta data entries * * @param tsdb The TSDB from the RPC router * @param query The query for this request */ private void handleTSMeta(final TSDB tsdb, final HttpQuery query) { final HttpMethod method = query.getAPIMethod(); // GET if (method == HttpMethod.GET) { String tsuid = null; if (query.hasQueryStringParam("tsuid")) { tsuid = query.getQueryStringParam("tsuid"); try { final TSMeta meta = TSMeta.getTSMeta(tsdb, tsuid).joinUninterruptibly(); if (meta != null) { query.sendReply(query.serializer().formatTSMetaV1(meta)); } else { throw new BadRequestException( HttpResponseStatus.NOT_FOUND, "Could not find Timeseries meta data"); } } catch (NoSuchUniqueName e) { // this would only happen if someone deleted a UID but left the // the timeseries meta data throw new BadRequestException( HttpResponseStatus.NOT_FOUND, "Unable to find one of the UIDs", e); } catch (BadRequestException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } else { String mquery = query.getRequiredQueryStringParam("m"); // m is of the following forms: // metric[{tag=value,...}] // where the parts in square brackets `[' .. `]' are optional. final HashMap<String, String> tags = new HashMap<String, String>(); String metric = null; try { metric = Tags.parseWithMetric(mquery, tags); } catch (IllegalArgumentException e) { throw new BadRequestException(e); } final TSUIDQuery tsuid_query = new TSUIDQuery(tsdb, metric, tags); try { final List<TSMeta> tsmetas = tsuid_query.getTSMetas().joinUninterruptibly(); query.sendReply(query.serializer().formatTSMetaListV1(tsmetas)); } catch (NoSuchUniqueName e) { throw new BadRequestException( HttpResponseStatus.NOT_FOUND, "Unable to find one of the UIDs", e); } catch (BadRequestException e) { throw e; } catch (RuntimeException e) { throw new BadRequestException(e); } catch (Exception e) { throw new RuntimeException(e); } } // POST / PUT } else if (method == HttpMethod.POST || method == HttpMethod.PUT) { final TSMeta meta; if (query.hasContent()) { meta = query.serializer().parseTSMetaV1(); } else { meta = this.parseTSMetaQS(query); } /** * Storage callback used to determine if the storage call was successful or not. Also returns * the updated object from storage. */ class SyncCB implements Callback<Deferred<TSMeta>, Boolean> { @Override public Deferred<TSMeta> call(Boolean success) throws Exception { if (!success) { throw new BadRequestException( HttpResponseStatus.INTERNAL_SERVER_ERROR, "Failed to save the TSMeta to storage", "This may be caused by another process modifying storage data"); } return TSMeta.getTSMeta(tsdb, meta.getTSUID()); } } if (meta.getTSUID() == null || meta.getTSUID().isEmpty()) { // we got a JSON object without TSUID. Try to find a timeseries spec of // the form "m": "metric{tagk=tagv,...}" final String metric = query.getRequiredQueryStringParam("m"); final boolean create = query.getQueryStringParam("create") != null && query.getQueryStringParam("create").equals("true"); final String tsuid = getTSUIDForMetric(metric, tsdb); class WriteCounterIfNotPresentCB implements Callback<Boolean, Boolean> { @Override public Boolean call(Boolean exists) throws Exception { if (!exists && create) { final PutRequest put = new PutRequest( tsdb.metaTable(), UniqueId.stringToUid(tsuid), TSMeta.FAMILY(), TSMeta.COUNTER_QUALIFIER(), Bytes.fromLong(0)); tsdb.getClient().put(put); } return exists; } } try { // Check whether we have a TSMeta stored already final boolean exists = TSMeta.metaExistsInStorage(tsdb, tsuid).joinUninterruptibly(); // set TSUID meta.setTSUID(tsuid); if (!exists && create) { // Write 0 to counter column if not present TSMeta.counterExistsInStorage(tsdb, UniqueId.stringToUid(tsuid)) .addCallback(new WriteCounterIfNotPresentCB()) .joinUninterruptibly(); // set TSUID final Deferred<TSMeta> process_meta = meta.storeNew(tsdb).addCallbackDeferring(new SyncCB()); final TSMeta updated_meta = process_meta.joinUninterruptibly(); tsdb.indexTSMeta(updated_meta); tsdb.processTSMetaThroughTrees(updated_meta); query.sendReply(query.serializer().formatTSMetaV1(updated_meta)); } else if (exists) { final Deferred<TSMeta> process_meta = meta.syncToStorage(tsdb, method == HttpMethod.PUT) .addCallbackDeferring(new SyncCB()); final TSMeta updated_meta = process_meta.joinUninterruptibly(); tsdb.indexTSMeta(updated_meta); query.sendReply(query.serializer().formatTSMetaV1(updated_meta)); } else { throw new BadRequestException( "Could not find TSMeta, specify \"create=true\" to create a new one."); } } catch (IllegalStateException e) { query.sendStatusOnly(HttpResponseStatus.NOT_MODIFIED); } catch (IllegalArgumentException e) { throw new BadRequestException(e); } catch (BadRequestException e) { throw e; } catch (NoSuchUniqueName e) { // this would only happen if someone deleted a UID but left the // the timeseries meta data throw new BadRequestException( HttpResponseStatus.NOT_FOUND, "Unable to find one or more UIDs", e); } catch (Exception e) { throw new RuntimeException(e); } } else { try { final Deferred<TSMeta> process_meta = meta.syncToStorage(tsdb, method == HttpMethod.PUT).addCallbackDeferring(new SyncCB()); final TSMeta updated_meta = process_meta.joinUninterruptibly(); tsdb.indexTSMeta(updated_meta); query.sendReply(query.serializer().formatTSMetaV1(updated_meta)); } catch (IllegalStateException e) { query.sendStatusOnly(HttpResponseStatus.NOT_MODIFIED); } catch (IllegalArgumentException e) { throw new BadRequestException(e); } catch (NoSuchUniqueName e) { // this would only happen if someone deleted a UID but left the // the timeseries meta data throw new BadRequestException( HttpResponseStatus.NOT_FOUND, "Unable to find one or more UIDs", e); } catch (Exception e) { throw new RuntimeException(e); } } // DELETE } else if (method == HttpMethod.DELETE) { final TSMeta meta; if (query.hasContent()) { meta = query.serializer().parseTSMetaV1(); } else { meta = this.parseTSMetaQS(query); } try { meta.delete(tsdb); tsdb.deleteTSMeta(meta.getTSUID()); } catch (IllegalArgumentException e) { throw new BadRequestException("Unable to delete TSMeta information", e); } query.sendStatusOnly(HttpResponseStatus.NO_CONTENT); } else { throw new BadRequestException( HttpResponseStatus.METHOD_NOT_ALLOWED, "Method not allowed", "The HTTP method [" + method.getName() + "] is not permitted for this endpoint"); } }
/** * Handles CRUD calls to individual UIDMeta data entries * * @param tsdb The TSDB from the RPC router * @param query The query for this request */ private void handleUIDMeta(final TSDB tsdb, final HttpQuery query) { final HttpMethod method = query.getAPIMethod(); // GET if (method == HttpMethod.GET) { final String uid = query.getRequiredQueryStringParam("uid"); final UniqueIdType type = UniqueId.stringToUniqueIdType(query.getRequiredQueryStringParam("type")); try { final UIDMeta meta = UIDMeta.getUIDMeta(tsdb, type, uid).joinUninterruptibly(); query.sendReply(query.serializer().formatUidMetaV1(meta)); } catch (NoSuchUniqueId e) { throw new BadRequestException( HttpResponseStatus.NOT_FOUND, "Could not find the requested UID", e); } catch (Exception e) { throw new RuntimeException(e); } // POST } else if (method == HttpMethod.POST || method == HttpMethod.PUT) { final UIDMeta meta; if (query.hasContent()) { meta = query.serializer().parseUidMetaV1(); } else { meta = this.parseUIDMetaQS(query); } /** * Storage callback used to determine if the storage call was successful or not. Also returns * the updated object from storage. */ class SyncCB implements Callback<Deferred<UIDMeta>, Boolean> { @Override public Deferred<UIDMeta> call(Boolean success) throws Exception { if (!success) { throw new BadRequestException( HttpResponseStatus.INTERNAL_SERVER_ERROR, "Failed to save the UIDMeta to storage", "This may be caused by another process modifying storage data"); } return UIDMeta.getUIDMeta(tsdb, meta.getType(), meta.getUID()); } } try { final Deferred<UIDMeta> process_meta = meta.syncToStorage(tsdb, method == HttpMethod.PUT).addCallbackDeferring(new SyncCB()); final UIDMeta updated_meta = process_meta.joinUninterruptibly(); tsdb.indexUIDMeta(updated_meta); query.sendReply(query.serializer().formatUidMetaV1(updated_meta)); } catch (IllegalStateException e) { query.sendStatusOnly(HttpResponseStatus.NOT_MODIFIED); } catch (IllegalArgumentException e) { throw new BadRequestException(e); } catch (NoSuchUniqueId e) { throw new BadRequestException( HttpResponseStatus.NOT_FOUND, "Could not find the requested UID", e); } catch (Exception e) { throw new RuntimeException(e); } // DELETE } else if (method == HttpMethod.DELETE) { final UIDMeta meta; if (query.hasContent()) { meta = query.serializer().parseUidMetaV1(); } else { meta = this.parseUIDMetaQS(query); } try { meta.delete(tsdb).joinUninterruptibly(); tsdb.deleteUIDMeta(meta); } catch (IllegalArgumentException e) { throw new BadRequestException("Unable to delete UIDMeta information", e); } catch (NoSuchUniqueId e) { throw new BadRequestException( HttpResponseStatus.NOT_FOUND, "Could not find the requested UID", e); } catch (Exception e) { throw new RuntimeException(e); } query.sendStatusOnly(HttpResponseStatus.NO_CONTENT); } else { throw new BadRequestException( HttpResponseStatus.METHOD_NOT_ALLOWED, "Method not allowed", "The HTTP method [" + method.getName() + "] is not permitted for this endpoint"); } }
/** * Closes this scanner (don't forget to call this when you're done with it!). * * <p>Closing a scanner already closed has no effect. * * @return a deferred object that indicates the completion of the request */ public RowResultIterator close() throws Exception { Deferred<RowResultIterator> d = asyncScanner.close(); return d.join(asyncScanner.scanRequestTimeout); }
@Override public void execute(final Tuple tuple) { List<IAsyncHBaseFieldMapper> mappers = mapper.getFieldMappers(); ArrayList<Deferred<Object>> requests = new ArrayList<>(mappers.size()); for (IAsyncHBaseFieldMapper fieldMapper : mappers) { switch (fieldMapper.getRpcType()) { case PUT: requests.add(client.put(fieldMapper.getPutRequest(tuple))); break; case INCR: requests.add( client .atomicIncrement(fieldMapper.getIncrementRequest(tuple)) // Dummy callback to cast long to Object .addCallback(incrCastCallback)); break; case DELETE: requests.add(client.delete(fieldMapper.getDeleteRequest(tuple))); break; case GET: requests.add( client .get(fieldMapper.getGetRequest(tuple)) // Dummy callback to cast ArrayList<KeyValue> to Object .addCallback(getCastCallback)); break; } } Deferred<ArrayList<Object>> results = Deferred.groupInOrder(requests); if (throttle) { log.warn("Throttling..."); long throttle_time = System.nanoTime(); try { results.joinUninterruptibly(this.timeout); this.collector.ack(tuple); } catch (Exception ex) { log.error("AsyncHBase exception : " + ex.toString()); this.collector.fail(tuple); } finally { throttle_time = System.nanoTime() - throttle_time; if (throttle_time < 1000000000L) { log.info("Got throttled for only " + throttle_time + "ns, sleeping a bit now"); try { Thread.sleep(1000); } catch (InterruptedException ex) { log.error("AsyncHBase exception : " + ex.toString()); } } log.info("Done throttling..."); this.throttle = false; } } else if (!this.async) { try { this.collector.emit(results.joinUninterruptibly(this.timeout)); this.collector.ack(tuple); } catch (Exception ex) { log.error("AsyncHBase exception : " + ex.toString()); this.collector.fail(tuple); } this.collector.ack(tuple); } else { results.addCallbacks(new SuccessCallback(tuple), new ErrorCallback(tuple)); } }
/** * Register a callback and an "errback". * * <p>This has the exact same effect as {@link Deferred#addCallbacks(Callback, Callback)} keeps * the type information "correct" when the callback and errback return a {@code Deferred}. * * @param d The {@code Deferred} we want to add the callback and errback to. * @param cb The callback to register. * @param eb The errback to register. * @return {@code d} with an "updated" type. */ @SuppressWarnings("unchecked") public static <T, R, D extends Deferred<R>, E> Deferred<R> addCallbacksDeferring( final Deferred<T> d, final Callback<D, T> cb, final Callback<D, E> eb) { return d.addCallbacks((Callback<R, T>) ((Object) cb), (Callback<R, E>) ((Object) eb)); }