use of io.deephaven.web.client.api.barrage.stream.ResponseStreamWrapper in project deephaven-core by deephaven.
the class RequestBatcher method sendRequest.
public Promise<Void> sendRequest() {
return new Promise<>((resolve, reject) -> {
// calling buildRequest will change the state of each table, so we first check what the state was
// of each table before we do that, in case we are actually not making a call to the server
ClientTableState prevState = table.lastVisibleState();
final BatchTableRequest request = buildRequest();
// let callbacks peek at entire request about to be sent.
for (JsConsumer<BatchTableRequest> send : onSend) {
send.apply(request);
}
onSend.clear();
sent = true;
if (request.getOpsList().length == 0) {
// This is like tableLoop below, except no failure is possible, since we already have the results
if (table.isAlive()) {
final ClientTableState active = table.state();
assert active.isRunning() : active;
boolean sortChanged = !prevState.getSorts().equals(active.getSorts());
boolean filterChanged = !prevState.getFilters().equals(active.getFilters());
boolean customColumnChanged = !prevState.getCustomColumns().equals(active.getCustomColumns());
table.fireEvent(HasEventHandling.EVENT_REQUEST_SUCCEEDED);
// TODO think more about the order of events, and what kinds of things one might bind to each
if (sortChanged) {
table.fireEvent(JsTable.EVENT_SORTCHANGED);
}
if (filterChanged) {
table.fireEvent(JsTable.EVENT_FILTERCHANGED);
}
if (customColumnChanged) {
table.fireEvent(JsTable.EVENT_CUSTOMCOLUMNSCHANGED);
}
}
orphans.forEach(ClientTableState::cleanup);
resolve.onInvoke((Void) null);
// if there's no outgoing operation, user may have removed an operation and wants to return to where
// they left off
table.maybeReviveSubscription();
finished = true;
return;
}
final BatchOp operationHead = builder.getFirstOp();
assert operationHead != null : "A non-empty request must have a firstOp!";
final ClientTableState source = operationHead.getAppendTo();
assert source != null : "A non-empty request must have a source state!";
JsLog.debug("Sending request", LazyString.of(request), request, " based on ", this);
ResponseStreamWrapper<ExportedTableCreationResponse> batchStream = ResponseStreamWrapper.of(connection.tableServiceClient().batch(request, connection.metadata()));
batchStream.onData(response -> {
TableReference resultid = response.getResultId();
if (!resultid.hasTicket()) {
// thanks for telling us, but we don't at this time have a nice way to indicate this
return;
}
Ticket ticket = resultid.getTicket();
if (!response.getSuccess()) {
String fail = response.getErrorInfo();
// any table which has that state active should fire a failed event
ClientTableState state = allStates().filter(cts -> cts.getHandle().makeTicket().getTicket_asB64().equals(ticket.getTicket_asB64())).first();
if (state.isEmpty()) {
// nobody cares about this state anymore
JsLog.debug("Ignoring empty state", state);
return;
}
state.getHandle().setState(TableTicket.State.FAILED);
for (JsTable table : allInterestedTables().filter(t -> t.state() == state)) {
// fire the failed event
failTable(table, fail);
}
// mark state as failed (his has to happen after table is marked as failed, it will trigger rollback
state.setResolution(ClientTableState.ResolutionState.FAILED, fail);
return;
}
// find the state that applies to this ticket
ClientTableState state = allStates().filter(cts -> cts.getHandle().makeTicket().getTicket_asB64().equals(ticket.getTicket_asB64())).first();
if (state.isEmpty()) {
// response.
return;
}
// Before we mark it as successfully running and give it its new schema, track the previous CTS
// that each table was using. Identify which table to watch based on its current state, even if
// not visible, but then track the last visible state, so we know which events to fire.
Map<JsTable, ClientTableState> activeTablesAndStates = StreamSupport.stream(allInterestedTables().spliterator(), false).filter(JsTable::isAlive).filter(t -> t.state() == state).collect(Collectors.toMap(Function.identity(), JsTable::lastVisibleState));
// Mark the table as ready to go
state.applyTableCreationResponse(response);
state.forActiveTables(t -> t.maybeRevive(state));
state.setResolution(ClientTableState.ResolutionState.RUNNING);
// Let each table know that what has changed since it has a previous state
for (JsTable table : activeTablesAndStates.keySet()) {
ClientTableState lastVisibleState = activeTablesAndStates.get(table);
// fire any events that are necessary
boolean sortChanged = !lastVisibleState.getSorts().equals(state.getSorts());
boolean filterChanged = !lastVisibleState.getFilters().equals(state.getFilters());
boolean customColumnChanged = !lastVisibleState.getCustomColumns().equals(state.getCustomColumns());
table.fireEvent(HasEventHandling.EVENT_REQUEST_SUCCEEDED);
// TODO think more about the order of events, and what kinds of things one might bind to each
if (sortChanged) {
table.fireEvent(JsTable.EVENT_SORTCHANGED);
}
if (filterChanged) {
table.fireEvent(JsTable.EVENT_FILTERCHANGED);
}
if (customColumnChanged) {
table.fireEvent(JsTable.EVENT_CUSTOMCOLUMNSCHANGED);
}
}
});
batchStream.onEnd(status -> {
// request is complete
if (status.isOk()) {
resolve.onInvoke((Void) null);
} else {
failed(reject, status.getDetails());
}
// Tell anybody who was orphaned to check if they should release their subscriptions / handles
orphans.forEach(ClientTableState::cleanup);
});
});
}
use of io.deephaven.web.client.api.barrage.stream.ResponseStreamWrapper in project deephaven-core by deephaven.
the class JsInputTable method deleteTables.
public Promise<JsInputTable> deleteTables(JsTable[] tablesToDelete) {
if (tablesToDelete.length == 0) {
return Promise.resolve(this);
}
// for each table, make a view on that table of only key columns, then union the tables and drop together
final List<JsRunnable> cleanups = new ArrayList<>();
final Ticket ticketToDelete;
final Promise<?> failureToReport;
if (tablesToDelete.length == 1) {
JsTable onlyTable = tablesToDelete[0];
// don't try too hard to find matching columns, if it looks like we have a match go for it
if (onlyTable.getColumns().length == keys.length && onlyTable.findColumns(keys).length == keys.length) {
ticketToDelete = onlyTable.getHandle().makeTicket();
failureToReport = Promise.resolve((Object) null);
} else {
// view the only table
ticketToDelete = table.getConnection().getConfig().newTicket();
cleanups.add(() -> table.getConnection().releaseTicket(ticketToDelete));
SelectOrUpdateRequest view = new SelectOrUpdateRequest();
view.setSourceId(onlyTable.state().getHandle().makeTableReference());
view.setResultId(ticketToDelete);
view.setColumnSpecsList(keys);
failureToReport = Callbacks.grpcUnaryPromise(c -> table.getConnection().tableServiceClient().view(view, table.getConnection().metadata(), c::apply));
}
} else {
// there is more than one table here, construct a merge after making a view of each table
ticketToDelete = table.getConnection().getConfig().newTicket();
cleanups.add(() -> table.getConnection().releaseTicket(ticketToDelete));
BatchTableRequest batch = new BatchTableRequest();
for (int i = 0; i < tablesToDelete.length; i++) {
JsTable toDelete = tablesToDelete[i];
SelectOrUpdateRequest view = new SelectOrUpdateRequest();
view.setSourceId(toDelete.state().getHandle().makeTableReference());
view.setColumnSpecsList(keys);
batch.addOps(new Operation()).setView(view);
}
MergeTablesRequest mergeRequest = new MergeTablesRequest();
mergeRequest.setSourceIdsList(IntStream.range(0, tablesToDelete.length).mapToObj(i -> {
TableReference ref = new TableReference();
ref.setBatchOffset(i);
return ref;
}).toArray(TableReference[]::new));
mergeRequest.setResultId(ticketToDelete);
batch.addOps(new Operation()).setMerge(mergeRequest);
failureToReport = new Promise<>((resolve, reject) -> {
ResponseStreamWrapper<ExportedTableCreationResponse> wrapper = ResponseStreamWrapper.of(table.getConnection().tableServiceClient().batch(batch, table.getConnection().metadata()));
wrapper.onData(response -> {
// kill the promise on the first failure we see
if (!response.getSuccess()) {
reject.onInvoke(response.getErrorInfo());
}
});
wrapper.onEnd(status -> resolve.onInvoke((Object) null));
});
}
// perform the delete on the current input table
DeleteTableRequest deleteRequest = new DeleteTableRequest();
deleteRequest.setInputTable(table.getHeadHandle().makeTicket());
deleteRequest.setTableToRemove(ticketToDelete);
return Callbacks.grpcUnaryPromise(c -> {
table.getConnection().inputTableServiceClient().deleteTableFromInputTable(deleteRequest, table.getConnection().metadata(), c::apply);
}).then(success -> {
cleanups.forEach(JsRunnable::run);
return Promise.resolve(this);
}, err -> {
cleanups.forEach(JsRunnable::run);
// call
return (Promise) failureToReport.then(ignore -> Promise.reject(err));
});
}
Aggregations