Package org.apache.bookkeeper.client

Source Code of org.apache.bookkeeper.client.PendingReadOp

package org.apache.bookkeeper.client;

/*
*
* 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.
*
*/

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.Queue;
import org.apache.bookkeeper.client.AsyncCallback.ReadCallback;
import org.apache.bookkeeper.client.BKException.BKDigestMatchException;
import org.apache.bookkeeper.proto.BookieProtocol;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.ReadEntryCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBufferInputStream;

import java.io.IOException;

/**
* Sequence of entries of a ledger that represents a pending read operation.
* When all the data read has come back, the application callback is called.
* This class could be improved because we could start pushing data to the
* application as soon as it arrives rather than waiting for the whole thing.
*
*/

class PendingReadOp implements Enumeration<LedgerEntry>, ReadEntryCallback {
    Logger LOG = LoggerFactory.getLogger(PendingReadOp.class);

    Queue<LedgerEntry> seq;
    ReadCallback cb;
    Object ctx;
    LedgerHandle lh;
    long numPendingReads;
    long startEntryId;
    long endEntryId;

    PendingReadOp(LedgerHandle lh, long startEntryId, long endEntryId, ReadCallback cb, Object ctx) {

        seq = new ArrayDeque<LedgerEntry>((int) (endEntryId - startEntryId));
        this.cb = cb;
        this.ctx = ctx;
        this.lh = lh;
        this.startEntryId = startEntryId;
        this.endEntryId = endEntryId;
        numPendingReads = endEntryId - startEntryId + 1;
    }

    public void initiate() throws InterruptedException {
        long nextEnsembleChange = startEntryId, i = startEntryId;

        ArrayList<InetSocketAddress> ensemble = null;
        do {

            if(LOG.isDebugEnabled()) {
                LOG.debug("Acquiring lock: " + i);
            }

            lh.opCounterSem.acquire();

            if (i == nextEnsembleChange) {
                ensemble = lh.metadata.getEnsemble(i);
                nextEnsembleChange = lh.metadata.getNextEnsembleChange(i);
            }
            LedgerEntry entry = new LedgerEntry(lh.ledgerId, i);
            seq.add(entry);
            i++;
            sendRead(ensemble, entry, BKException.Code.ReadException);

        } while (i <= endEntryId);
    }

    void sendRead(ArrayList<InetSocketAddress> ensemble, LedgerEntry entry, int lastErrorCode) {
        if (entry.nextReplicaIndexToReadFrom >= lh.metadata.quorumSize) {
            // we are done, the read has failed from all replicas, just fail the
            // read
            submitCallback(lastErrorCode);
            return;
        }

        int bookieIndex = lh.distributionSchedule.getBookieIndex(entry.entryId, entry.nextReplicaIndexToReadFrom);
        entry.nextReplicaIndexToReadFrom++;
        lh.bk.bookieClient.readEntry(ensemble.get(bookieIndex), lh.ledgerId, entry.entryId,
                                     this, entry, BookieProtocol.FLAG_NONE);
    }

    void logErrorAndReattemptRead(LedgerEntry entry, String errMsg, int rc) {
        ArrayList<InetSocketAddress> ensemble = lh.metadata.getEnsemble(entry.entryId);
        int bookeIndex = lh.distributionSchedule.getBookieIndex(entry.entryId, entry.nextReplicaIndexToReadFrom - 1);
        LOG.error(errMsg + " while reading entry: " + entry.entryId + " ledgerId: " + lh.ledgerId + " from bookie: "
                  + ensemble.get(bookeIndex));
        sendRead(ensemble, entry, rc);
        return;
    }

    @Override
    public void readEntryComplete(int rc, long ledgerId, final long entryId, final ChannelBuffer buffer, Object ctx) {
        final LedgerEntry entry = (LedgerEntry) ctx;

        if (rc != BKException.Code.OK) {
            logErrorAndReattemptRead(entry, "Error: " + BKException.getMessage(rc), rc);
            return;
        }

        ChannelBufferInputStream is;
        try {
            is = lh.macManager.verifyDigestAndReturnData(entryId, buffer);
        } catch (BKDigestMatchException e) {
            logErrorAndReattemptRead(entry, "Mac mismatch", BKException.Code.DigestMatchException);
            return;
        }

        entry.entryDataStream = is;

        /*
         * The length is a long and it is the last field of the metadata of an entry.
         * Consequently, we have to subtract 8 from METADATA_LENGTH to get the length.
         */
        entry.length = buffer.getLong(DigestManager.METADATA_LENGTH - 8);

        numPendingReads--;
        if (numPendingReads == 0) {
            submitCallback(BKException.Code.OK);
        }

        if(LOG.isDebugEnabled()) {
            LOG.debug("Releasing lock: " + entryId);
        }

        lh.opCounterSem.release();

        if(numPendingReads < 0)
            LOG.error("Read too many values");
    }

    private void submitCallback(int code) {
        cb.readComplete(code, lh, PendingReadOp.this, PendingReadOp.this.ctx);
    }
    public boolean hasMoreElements() {
        return !seq.isEmpty();
    }

    public LedgerEntry nextElement() throws NoSuchElementException {
        return seq.remove();
    }

    public int size() {
        return seq.size();
    }
}
TOP

Related Classes of org.apache.bookkeeper.client.PendingReadOp

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.