/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.bookkeeper.client;
import java.util.HashSet;
import java.util.Set;
import org.apache.bookkeeper.client.AsyncCallback.AddLacCallback;
import org.apache.bookkeeper.net.BookieSocketAddress;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.WriteLacCallback;
import org.apache.bookkeeper.stats.OpStatsLogger;
import org.jboss.netty.buffer.ChannelBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This represents a pending WriteLac operation. When it has got
* success from Ack Quorum bookies, sends success back to the application,
* otherwise failure is sent back to the caller.
*
* This is an optional protocol operations to facilitate tailing readers
* to be up to date with the writer. This is best effort to get latest LAC
* from bookies, and doesn't affect the correctness of the protocol.
*/
class PendingWriteLacOp implements WriteLacCallback {
private final static Logger LOG = LoggerFactory.getLogger(PendingWriteLacOp.class);
ChannelBuffer toSend;
AddLacCallback cb;
long lac;
Object ctx;
Set<Integer> writeSet;
Set<Integer> receivedResponseSet;
DistributionSchedule.AckSet ackSet;
boolean completed = false;
int lastSeenError = BKException.Code.WriteException;
LedgerHandle lh;
OpStatsLogger putLacOpLogger;
PendingWriteLacOp(LedgerHandle lh, AddLacCallback cb, Object ctx) {
this.lh = lh;
this.cb = cb;
this.ctx = ctx;
this.lac = LedgerHandle.INVALID_ENTRY_ID;
ackSet = lh.distributionSchedule.getAckSet();
putLacOpLogger = lh.bk.getWriteLacOpLogger();
}
void setLac(long lac) {
this.lac = lac;
this.writeSet = new HashSet<Integer>(lh.distributionSchedule.getWriteSet(lac));
this.receivedResponseSet = new HashSet<Integer>(writeSet);
}
void sendWriteLacRequest(int bookieIndex) {
lh.bk.bookieClient.writeLac(lh.metadata.currentEnsemble.get(bookieIndex), lh.ledgerId, lh.ledgerKey,
lac, toSend, this, bookieIndex);
}
void initiate(ChannelBuffer toSend) {
this.toSend = toSend;
for (int bookieIndex: writeSet) {
sendWriteLacRequest(bookieIndex);
}
}
@Override
public void writeLacComplete(int rc, long ledgerId, BookieSocketAddress addr, Object ctx) {
int bookieIndex = (Integer) ctx;
if (completed) {
return;
}
if (BKException.Code.OK != rc) {
lastSeenError = rc;
}
// We got response.
receivedResponseSet.remove(bookieIndex);
if (rc == BKException.Code.OK) {
if (ackSet.addBookieAndCheck(bookieIndex) && !completed) {
completed = true;
cb.addLacComplete(rc, lh, ctx);
return;
}
} else {
LOG.warn("WriteLac did not succeed: Ledger {} on {}", new Object[] { ledgerId, addr });
}
if(receivedResponseSet.isEmpty()){
completed = true;
cb.addLacComplete(lastSeenError, lh, ctx);
}
}
}