/** * 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 org.apache.bookkeeper.client.BKException.BKDigestMatchException; import org.apache.bookkeeper.client.DigestManager.RecoveryData; import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.ReadEntryCallback; import org.apache.bookkeeper.proto.BookieProtocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.jboss.netty.buffer.ChannelBuffer; /** * This class encapsulated the read last confirmed operation. * */ class ReadLastConfirmedOp implements ReadEntryCallback { static final Logger LOG = LoggerFactory.getLogger(ReadLastConfirmedOp.class); LedgerHandle lh; int numResponsesPending; RecoveryData maxRecoveredData; volatile boolean completed = false; int lastSeenError = BKException.Code.ReadException; LastConfirmedDataCallback cb; final DistributionSchedule.QuorumCoverageSet coverageSet; /** * Wrapper to get all recovered data from the request */ interface LastConfirmedDataCallback { public void readLastConfirmedDataComplete(int rc, RecoveryData data); } public ReadLastConfirmedOp(LedgerHandle lh, LastConfirmedDataCallback cb) { this.cb = cb; this.maxRecoveredData = new RecoveryData(LedgerHandle.INVALID_ENTRY_ID, 0); this.lh = lh; this.numResponsesPending = lh.metadata.getEnsembleSize(); this.coverageSet = lh.distributionSchedule.getCoverageSet(); } public void initiate() { for (int i = 0; i < lh.metadata.currentEnsemble.size(); i++) { lh.bk.bookieClient.readEntry(lh.metadata.currentEnsemble.get(i), lh.ledgerId, BookieProtocol.LAST_ADD_CONFIRMED, this, i); } } public void initiateWithFencing() { for (int i = 0; i < lh.metadata.currentEnsemble.size(); i++) { lh.bk.bookieClient.readEntryAndFenceLedger(lh.metadata.currentEnsemble.get(i), lh.ledgerId, lh.ledgerKey, BookieProtocol.LAST_ADD_CONFIRMED, this, i); } } public synchronized void readEntryComplete(final int rc, final long ledgerId, final long entryId, final ChannelBuffer buffer, final Object ctx) { int bookieIndex = (Integer) ctx; numResponsesPending--; boolean heardValidResponse = false; if (rc == BKException.Code.OK) { try { RecoveryData recoveryData = lh.macManager.verifyDigestAndReturnLastConfirmed(buffer); if (recoveryData.lastAddConfirmed > maxRecoveredData.lastAddConfirmed) { maxRecoveredData = recoveryData; } heardValidResponse = true; } catch (BKDigestMatchException e) { // Too bad, this bookie didn't give us a valid answer, we // still might be able to recover though so continue LOG.error("Mac mismatch for ledger: " + ledgerId + ", entry: " + entryId + " while reading last entry from bookie: " + lh.metadata.currentEnsemble.get(bookieIndex)); } } if (rc == BKException.Code.NoSuchLedgerExistsException || rc == BKException.Code.NoSuchEntryException) { // this still counts as a valid response, e.g., if the client crashed without writing any entry heardValidResponse = true; } if (rc == BKException.Code.UnauthorizedAccessException && !completed) { cb.readLastConfirmedDataComplete(rc, maxRecoveredData); completed = true; } if (!heardValidResponse && BKException.Code.OK != rc) { lastSeenError = rc; } // other return codes dont count as valid responses if (heardValidResponse && coverageSet.addBookieAndCheckCovered(bookieIndex) && !completed) { completed = true; LOG.debug("Read Complete with enough validResponses for ledger: {}, entry: {}", ledgerId, entryId); cb.readLastConfirmedDataComplete(BKException.Code.OK, maxRecoveredData); return; } if (numResponsesPending == 0 && !completed) { // Have got all responses back but was still not enough, just fail the operation LOG.error("While readLastConfirmed ledger: " + ledgerId + " did not hear success responses from all quorums"); cb.readLastConfirmedDataComplete(lastSeenError, maxRecoveredData); } } }