This is an automated email from the ASF dual-hosted git repository. zhaijia pushed a commit to branch branch-4.6 in repository https://gitbox.apache.org/repos/asf/bookkeeper.git
The following commit(s) were added to refs/heads/branch-4.6 by this push: new 17cb7bc ISSUE #787: Client should be able to get ledger metadata from Handles. 17cb7bc is described below commit 17cb7bc7e4a76393437580f1454fdff40346919a Author: Sijie Guo <si...@apache.org> AuthorDate: Fri Dec 1 22:13:49 2017 +0800 ISSUE #787: Client should be able to get ledger metadata from Handles. Descriptions of the changes in this PR: - create a `LedgerMetadata` interface for exposing public information - add `getLedgerMetadata` to Handle, so both WriteHandle and ReadHandle are able to access the metadata. Author: Sijie Guo <si...@apache.org> Reviewers: Ivan Kelly <iv...@apache.org>, Enrico Olivelli <eolive...@gmail.com>, Jia Zhai <None> This closes #788 from sijie/ledger_metadata_interface, closes #787 (cherry picked from commit 5f3496f740629ba4ea0d0abb3272ca2e6d8ccba7) Signed-off-by: Jia Zhai <zhai...@apache.org> --- .../org/apache/bookkeeper/client/LedgerHandle.java | 5 +- .../apache/bookkeeper/client/LedgerMetadata.java | 39 +++++-- .../org/apache/bookkeeper/client/LedgerOpenOp.java | 10 +- .../org/apache/bookkeeper/client/api/Handle.java | 11 ++ .../bookkeeper/client/api/LedgerMetadata.java | 121 +++++++++++++++++++++ .../bookkeeper/client/LedgerMetadataTest.java | 33 ++++++ .../apache/bookkeeper/client/TestSequenceRead.java | 2 +- .../main/resources/bookkeeper/findbugsExclude.xml | 4 + 8 files changed, 206 insertions(+), 19 deletions(-) diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerHandle.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerHandle.java index ca6cb89..2207fc5 100644 --- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerHandle.java +++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerHandle.java @@ -231,10 +231,9 @@ public class LedgerHandle implements WriteHandle { } /** - * Get the LedgerMetadata - * - * @return LedgerMetadata for the LedgerHandle + * {@inheritDoc} */ + @Override public LedgerMetadata getLedgerMetadata() { return metadata; } diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerMetadata.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerMetadata.java index 44c8ce7..180c59e 100644 --- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerMetadata.java +++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerMetadata.java @@ -35,9 +35,11 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.NavigableMap; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; +import org.apache.bookkeeper.client.api.DigestType; import org.apache.bookkeeper.net.BookieSocketAddress; import org.apache.bookkeeper.proto.DataFormats.LedgerMetadataFormat; import org.apache.bookkeeper.versioning.Version; @@ -50,7 +52,7 @@ import org.slf4j.LoggerFactory; * * <p>It provides parsing and serialization methods of such metadata. */ -public class LedgerMetadata { +public class LedgerMetadata implements org.apache.bookkeeper.client.api.LedgerMetadata { static final Logger LOG = LoggerFactory.getLogger(LedgerMetadata.class); private static final String closed = "CLOSED"; @@ -77,7 +79,7 @@ public class LedgerMetadata { private boolean storeSystemtimeAsLedgerCreationTime; private LedgerMetadataFormat.State state; - private SortedMap<Long, ArrayList<BookieSocketAddress>> ensembles = + private TreeMap<Long, ArrayList<BookieSocketAddress>> ensembles = new TreeMap<Long, ArrayList<BookieSocketAddress>>(); ArrayList<BookieSocketAddress> currentEnsemble; volatile Version version = Version.NEW; @@ -173,30 +175,35 @@ public class LedgerMetadata { * @return SortedMap of Ledger Fragments and the corresponding * bookie ensembles that store the entries. */ - public SortedMap<Long, ArrayList<BookieSocketAddress>> getEnsembles() { + public TreeMap<Long, ArrayList<BookieSocketAddress>> getEnsembles() { return ensembles; } - void setEnsembles(SortedMap<Long, ArrayList<BookieSocketAddress>> ensembles) { + @Override + public NavigableMap<Long, ? extends List<BookieSocketAddress>> getAllEnsembles() { + return ensembles; + } + + void setEnsembles(TreeMap<Long, ArrayList<BookieSocketAddress>> ensembles) { this.ensembles = ensembles; } + @Override public int getEnsembleSize() { return ensembleSize; } + @Override public int getWriteQuorumSize() { return writeQuorumSize; } + @Override public int getAckQuorumSize() { return ackQuorumSize; } - /** - * Get the creation timestamp of the ledger - * @return - */ + @Override public long getCtime() { return ctime; } @@ -221,18 +228,21 @@ public class LedgerMetadata { return Arrays.copyOf(password, password.length); } - BookKeeper.DigestType getDigestType() { + @Override + public DigestType getDigestType() { if (digestType.equals(LedgerMetadataFormat.DigestType.HMAC)) { - return BookKeeper.DigestType.MAC; + return DigestType.MAC; } else { - return BookKeeper.DigestType.CRC32; + return DigestType.CRC32; } } + @Override public long getLastEntryId() { return lastEntryId; } + @Override public long getLength() { return length; } @@ -241,6 +251,7 @@ public class LedgerMetadata { this.length = length; } + @Override public boolean isClosed() { return state == LedgerMetadataFormat.State.CLOSED; } @@ -279,6 +290,11 @@ public class LedgerMetadata { return ensembles.get(ensembles.headMap(entryId + 1).lastKey()); } + @Override + public List<BookieSocketAddress> getEnsembleAt(long entryId) { + return getEnsemble(entryId); + } + /** * the entry id greater than the given entry-id at which the next ensemble change takes * place @@ -296,6 +312,7 @@ public class LedgerMetadata { } } + @Override public Map<String, byte[]> getCustomMetadata() { return this.customMetadata; } diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerOpenOp.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerOpenOp.java index 910f33a..256cd59 100644 --- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerOpenOp.java +++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/LedgerOpenOp.java @@ -21,6 +21,8 @@ package org.apache.bookkeeper.client; +import static org.apache.bookkeeper.client.BookKeeper.DigestType.fromApiDigestType; + import java.util.Arrays; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -128,7 +130,7 @@ class LedgerOpenOp implements GenericCallback<LedgerMetadata> { final byte[] passwd; DigestType digestType = enableDigestAutodetection - ? metadata.getDigestType() + ? fromApiDigestType(metadata.getDigestType()) : suggestedDigestType; /* For an administrative open, the default passwords @@ -136,7 +138,7 @@ class LedgerOpenOp implements GenericCallback<LedgerMetadata> { * already contains passwords, use these instead. */ if (administrativeOpen && metadata.hasPassword()) { passwd = metadata.getPassword(); - digestType = metadata.getDigestType(); + digestType = fromApiDigestType(metadata.getDigestType()); } else { passwd = this.passwd; @@ -146,7 +148,7 @@ class LedgerOpenOp implements GenericCallback<LedgerMetadata> { openComplete(BKException.Code.UnauthorizedAccessException, null); return; } - if (digestType != metadata.getDigestType()) { + if (digestType != fromApiDigestType(metadata.getDigestType())) { LOG.error("Provided digest does not match that in metadata"); openComplete(BKException.Code.DigestMatchException, null); return; @@ -276,7 +278,7 @@ class LedgerOpenOp implements GenericCallback<LedgerMetadata> { return; } - LedgerOpenOp op = new LedgerOpenOp(bk, builderLedgerId, DigestType.fromApiDigestType(builderDigestType), + LedgerOpenOp op = new LedgerOpenOp(bk, builderLedgerId, fromApiDigestType(builderDigestType), builderPassword, cb, null); ReentrantReadWriteLock closeLock = bk.getCloseLock(); closeLock.readLock().lock(); diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/api/Handle.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/api/Handle.java index 326f2e6..f520e61 100644 --- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/api/Handle.java +++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/api/Handle.java @@ -56,6 +56,17 @@ public interface Handle extends AutoCloseable { } /** + * Returns the metadata of this ledger. + * + * <p>This call only retrieves the metadata cached locally. If there is any metadata updated, the read + * handle will receive the metadata updates and update the metadata locally. The metadata notification + * can be deplayed, so it is possible you can receive a stale copy of ledger metadata from this call. + * + * @return the metadata of this ledger. + */ + LedgerMetadata getLedgerMetadata(); + + /** * Asynchronous close, any adds in flight will return errors. * * <p>Closing a ledger will ensure that all clients agree on what the last diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/api/LedgerMetadata.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/api/LedgerMetadata.java new file mode 100644 index 0000000..dc2deb6 --- /dev/null +++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/api/LedgerMetadata.java @@ -0,0 +1,121 @@ +/* + * 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.api; + +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import org.apache.bookkeeper.common.annotation.InterfaceAudience.LimitedPrivate; +import org.apache.bookkeeper.common.annotation.InterfaceStability.Unstable; +import org.apache.bookkeeper.net.BookieSocketAddress; + +/** + * Represents the client-side metadata of a ledger. It is immutable. + * + * @since 4.6 + */ +@LimitedPrivate +@Unstable +public interface LedgerMetadata { + + /** + * Returns the ensemble size of this ledger. + * + * @return the ensemble size of this ledger. + */ + int getEnsembleSize(); + + /** + * Returns the write quorum size of this ledger. + * + * @return the write quorum size of this ledger. + */ + int getWriteQuorumSize(); + + /** + * Returns the ack quorum size of this ledger. + * + * @return the ack quorum size of this ledger. + */ + int getAckQuorumSize(); + + /** + * Returns the last entry id of this ledger. + * + * <p>If this ledger is not sealed {@link #isClosed()}, it returns {@code -1L}. + * + * @return the last entry id of this ledger if it is sealed, otherwise -1. + */ + long getLastEntryId(); + + /** + * Returns the length of this ledger. + * + * <p>If this ledger is not sealed {@link #isClosed()}, it returns {@code 0}. + * + * @return the length of this ledger if it is sealed, otherwise 0. + */ + long getLength(); + + /** + * Returns the digest type used by this ledger. + * + * @return the digest type used by this ledger. + */ + DigestType getDigestType(); + + /** + * Returns the creation timestamp of this ledger. + * + * @return the creation timestamp of this ledger. + */ + long getCtime(); + + /** + * Returns whether the ledger is sealed or not. + * + * @return true if the ledger is sealed, otherwise false. + */ + boolean isClosed(); + + /** + * Returns the custom metadata stored with the ledgers. + * + * @return the custom metadata stored with the ledgers. + */ + Map<String, byte[]> getCustomMetadata(); + + /** + * Returns the ensemble at the given {@code entryId}. + * + * @param entryId the entry id to retrieve its ensemble information + * @return the ensemble which contains the given {@code entryId}. + */ + List<BookieSocketAddress> getEnsembleAt(long entryId); + + /** + * Returns all the ensembles of this entry. + * + * @return all the ensembles of this entry. + */ + NavigableMap<Long, ? extends List<BookieSocketAddress>> getAllEnsembles(); + + +} diff --git a/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/LedgerMetadataTest.java b/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/LedgerMetadataTest.java index d7b913f..a58147c 100644 --- a/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/LedgerMetadataTest.java +++ b/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/LedgerMetadataTest.java @@ -20,10 +20,13 @@ package org.apache.bookkeeper.client; import static com.google.common.base.Charsets.UTF_8; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.util.Collections; +import java.util.NoSuchElementException; import org.apache.bookkeeper.client.BookKeeper.DigestType; import org.apache.bookkeeper.proto.DataFormats.LedgerMetadataFormat; import org.junit.Test; @@ -36,6 +39,36 @@ public class LedgerMetadataTest { private static final byte[] passwd = "testPasswd".getBytes(UTF_8); @Test + public void testGetters() { + org.apache.bookkeeper.client.api.LedgerMetadata metadata = new LedgerMetadata( + 3, + 2, + 1, + DigestType.CRC32, + passwd, + Collections.emptyMap(), + false); + + assertEquals(3, metadata.getEnsembleSize()); + assertEquals(2, metadata.getWriteQuorumSize()); + assertEquals(1, metadata.getAckQuorumSize()); + assertEquals(org.apache.bookkeeper.client.api.DigestType.CRC32, metadata.getDigestType()); + assertEquals(Collections.emptyMap(), metadata.getCustomMetadata()); + assertEquals(-1L, metadata.getCtime()); + assertEquals(-1L, metadata.getLastEntryId()); + assertEquals(0, metadata.getLength()); + assertFalse(metadata.isClosed()); + assertTrue(metadata.getAllEnsembles().isEmpty()); + + try { + metadata.getEnsembleAt(99L); + fail("Should fail to retrieve ensemble if ensembles is empty"); + } catch (NoSuchElementException e) { + // expected + } + } + + @Test public void testStoreSystemtimeAsLedgerCtimeEnabled() throws Exception { LedgerMetadata lm = new LedgerMetadata( diff --git a/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/TestSequenceRead.java b/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/TestSequenceRead.java index 8e38487..05cf072 100644 --- a/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/TestSequenceRead.java +++ b/bookkeeper-server/src/test/java/org/apache/bookkeeper/client/TestSequenceRead.java @@ -55,7 +55,7 @@ public class TestSequenceRead extends BookKeeperClusterTestCase { final LedgerHandle lh = bkc.createLedger(3, 3, 3, digestType, passwd); // introduce duplicated bookies in an ensemble. SortedMap<Long, ArrayList<BookieSocketAddress>> ensembles = lh.getLedgerMetadata().getEnsembles(); - SortedMap<Long, ArrayList<BookieSocketAddress>> newEnsembles = new TreeMap<Long, ArrayList<BookieSocketAddress>>(); + TreeMap<Long, ArrayList<BookieSocketAddress>> newEnsembles = new TreeMap<>(); for (Map.Entry<Long, ArrayList<BookieSocketAddress>> entry : ensembles.entrySet()) { ArrayList<BookieSocketAddress> newList = new ArrayList<BookieSocketAddress>(entry.getValue().size()); BookieSocketAddress firstBookie = entry.getValue().get(0); diff --git a/buildtools/src/main/resources/bookkeeper/findbugsExclude.xml b/buildtools/src/main/resources/bookkeeper/findbugsExclude.xml index e239e67..2cc5ce7 100644 --- a/buildtools/src/main/resources/bookkeeper/findbugsExclude.xml +++ b/buildtools/src/main/resources/bookkeeper/findbugsExclude.xml @@ -51,6 +51,10 @@ <Bug pattern="NM_SAME_SIMPLE_NAME_AS_INTERFACE" /> </Match> <Match> + <Class name="org.apache.bookkeeper.client.LedgerMetadata" /> + <Bug pattern="NM_SAME_SIMPLE_NAME_AS_INTERFACE" /> + </Match> + <Match> <Class name="org.apache.bookkeeper.client.BookKeeper" /> <Bug pattern="NM_SAME_SIMPLE_NAME_AS_INTERFACE" /> </Match> -- To stop receiving notification emails like this one, please contact ['"commits@bookkeeper.apache.org" <commits@bookkeeper.apache.org>'].