bookkeeper-release-4.2.4/ 0000775 0000000 0000000 00000000000 12445073612 0015271 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/CHANGES.txt 0000664 0000000 0000000 00000130432 12445073612 0017105 0 ustar 00root root 0000000 0000000 Release 4.2.4 - 2015-01-12
Backward compatible changes:
BUGFIXES:
BOOKKEEPER-815: Ledger fence state is lost when the ledger file is evicted (Charles Xie via ivank)
BOOKKEEPER-799: Distribution schedule coverage sets don't take gaps in response lists into account when writequorum > ackquorum (ivank)
BOOKKEEPER-795: Race condition causes writes to hang if ledger is fenced (sijie via ivank)
IMPROVEMENTS:
BOOKKEEPER-800: Expose whether a ledger is closed or not (ivank)
Release 4.2.3 - 2014-06-27
Backward compatible changes:
BUGFIXES:
BOOKKEEPER-766: Update notice.txt files to include 2014 (ivank via fpj)
BOOKKEEPER-767: Allow loopback in tests (ivank via fpj)
BOOKKEEPER-765: bookkeeper script should fall back to java in path if JAVA_HOME is not set (ivank)
bookkeeper-server:
BOOKKEEPER-711: bookkeeper-daemon.sh will not remove the pid file one successful stop (vinay via sijie)
BOOKKEEPER-712: bookkeeper script should use 'java' from JAVA_HOME (vinay via sijie)
BOOKKEEPER-688: NPE exception in PerChannelBookieClient (ivank via sijie)
BOOKKEEPER-602: we should have request timeouts rather than channel timeout in PerChannelBookieClient (Aniruddha via sijie)
BOOKKEEPER-714: Logging channel exceptions in PerChannelBookieClient (sijie)
BOOKKEEPER-726: PerChannelBookieClient should print address that it failed to connect to when it fails to correct (ivank via sijie)
BOOKKEEPER-710: OpenLedgerNoRecovery should watch ensemble change. (sijie, ivank via fpj)
BOOKKEEPER-742: Fix for empty ledgers losing quorum. (ivank)
BOOKKEEPER-743: Periodic ledger check running too often as doc doesn't match implementation. (ivank)
BOOKKEEPER-744: Run the auditor bookie check periodically (ivank)
BOOKKEEPER-755: Incorrect number of seconds specified in a day (Joseph Redfern via fpj)
BOOKKEEPER-752: Deadlock on NIOServer shutdown (sijie via ivank)
BOOKKEEPER-673: Ledger length can be inaccurate in failure case (sijie via ivank)
BOOKKEEPER-751: Ensure all the bookkeeper callbacks not run under ledger handle lock (sijie via ivank)
BOOKKEEPER-745: Fix for false reports of ledger unreplication during rolling restarts. (ivank)
BOOKKEEPER-708: Shade protobuf library to avoid incompatible versions (ivank)
BOOKKEEPER-730: Shade pom file missing apache license header (ivank)
BOOKKEEPER-725: AutoRecoveryMain should exit with error code if deathwatcher finds dead thread (ivank)
BOOKKEEPER-750: Flake in BookieAutoRecoveryTest#testEmptyLedgerLosesQuorumEventually (ivank)
IMPROVEMENT:
BOOKKEEPER-747: Implement register/unregister LedgerMetadataListener in MSLedgerManagerFactory (fpj via sijie)
BOOKKEEPER-746: 5 new shell commands. List ledgers, list metadata, list underreplicated, show auditor and simpletest (ivank)
Release 4.2.2 - 2013-10-02
Backward compatible changes:
BUGFIXES:
BOOKKEEPER-635: jenkins build should highlight which lines of the patch cause raw analysis errors (ivank via sijie)
BOOKKEEPER-684: ZK logging is oververbose, can cause oom in tests (ivank via fpj)
bookkeeper-server:
BOOKKEEPER-559: Fix occasional failure in AuditorBookieTest (ivank)
BOOKKEEPER-556: BookieServerMXBean#getServerState makes no sense (ivank)
BOOKKEEPER-583: Read from a ReadOnlyBookie fails if index fileinfo is not in ledger cache (vinay via sijie)
BOOKKEEPER-585: Auditor logs noisily when a ledger has been deleted (ivank)
BOOKKEEPER-581: Ledger recovery doesn't work correctly when recovery adds force changing ensembles. (sijie via ivank)
BOOKKEEPER-595: Crash of inprocess autorecovery daemon should not take down the bookie (ivank)
BOOKKEEPER-596: Ledgers are gc'ed by mistake in MSLedgerManagerFactory. (sijie via ivank)
BOOKKEEPER-584: Data loss when ledger metadata is overwritten (sijie via ivank)
BOOKKEEPER-577: BookieFailureTest uses sync/wait()/notify() incorrectly (ivank)
BOOKKEEPER-626: BOOKIE_EXTRA_OPTS are added twice (vinay via fpj)
BOOKKEEPER-619: Bookie should not create local cookie files if zookeeper is uninitialized (ivank)
BOOKKEEPER-313: Bookkeeper shutdown call from Bookie thread is not shutting down server (vinay via ivank)
BOOKKEEPER-623: LedgerChecker should avoid segments of closed ledger with higher start entryId than closed entry. (vinay via sijie)
BOOKKEEPER-620: PerChannelBookieClient race during channel disconnect (ivank)
BOOKKEEPER-637: NoSuchEntry exception when reading an entry from a bookie should not print ERROR level message (mmerli via ivank)
BOOKKEEPER-257: Ability to list all ledgers (fpj via ivank)
BOOKKEEPER-636: Latest txn logs might be deleted in a race condition which is not recoverable if BK goes down before next txn log created. (vinay via ivank)
BOOKKEEPER-621: NPE in FileInfo.moveToNewLocation (ivank via sijie)
BOOKKEEPER-646: BookieShell readjournal command is throwing BufferUnderflowException (Rakesh via sijie)
BOOKKEEPER-652: Logger class name is wrong in LedgerCacheImpl.java (Rakesh via sijie)
BOOKKEEPER-625: On OutOfMemoryError in NIOServerFactory thread bookie should shutdown (vinay via ivank)
BOOKKEEPER-642: Bookie returns incorrect exitcode, ExitCode.ZK_REG_FAIL is getting overridden (Rakesh via ivank)
BOOKKEEPER-663: HierarchicalLedgerManager iterator is missing some ranges and the last ledger in the range (mmerli via ivank)
BOOKKEEPER-604: Ledger storage can log an exception if GC happens concurrently. (sijie & ivank via ivank)
BOOKKEEPER-667: Client write will fail with BadMetadataVersion in case of multiple Bookie failures with AutoRecovery enabled (sijie via ivank)
BOOKKEEPER-668: Race between PerChannelBookieClient#channelDisconnected() and disconnect() calls can make clients hang while add/reading entries in case of multiple bookie failures (sijie & ivank via ivank)
BOOKKEEPER-624: Reduce logs generated by ReplicationWorker (vinay via ivank)
BOOKKEEPER-660: Logs too noisy on NIOServerFactory when client drops a connection (mmerli via ivank)
BOOKKEEPER-632: AutoRecovery should consider read only bookies (vinay via ivank)
BOOKKEEPER-649: Race condition in sync ZKUtils.createFullPathOptimistic() (ivank)
BOOKKEEPER-580: improve close logic (sijie & ivank via ivank)
BOOKKEEPER-664: Compaction increases latency on journal writes (ivank & sijie via ivank)
BOOKKEEPER-679: Bookie should exit with non-zero if NIOServer crashes with Error (ivank)
BOOKKEEPER-669: Race condition in ledger deletion and eviction from cache (rakeshr via ivank)
BOOKKEEPER-446: BookKeeper.createLedger(..) should not mask the error with ZKException (sijie via ivank)
BOOKKEEPER-675: Log noise fixup before cutting 4.2.2 (ivank)
BOOKKEEPER-627: LedgerDirsMonitor is missing thread name (rakeshr via ivank)
BOOKKEEPER-685: Race in compaction algorithm from BOOKKEEPER-664 (ivank)
hedwig-server:
BOOKKEEPER-579: TestSubAfterCloseSub was put in a wrong package (sijie via ivank)
BOOKKEEPER-601: readahead cache size isn't updated correctly (sijie via fpj)
BOOKKEEPER-607: Filtered Messages Require ACK from Client Causes User Being Throttled Incorrectly Forever (sijie via ivank)
BOOKKEEPER-683: TestSubAfterCloseSub fails on 4.2 (jiannan via ivank)
hedwig-client:
BOOKKEEPER-598: Fails to compile - RESUBSCRIBE_EXCEPTION conflict (Matthew Farrellee via ivank)
BOOKKEEPER-603: Support Boost 1.53 for Hedwig Cpp Client (jiannan via ivank)
BOOKKEEPER-600: shouldClaim flag isn't cleared for hedwig multiplex java client (sijie via fpj)
NEW FEATURE:
BOOKKEEPER-562: Ability to tell if a ledger is closed or not (fpj via ivank)
IMPROVEMENT:
BOOKKEEPER-618: Better resolution of bookie address (ivank via fpj)
Release 4.2.1 - 2013-02-19
Backward compatible changes:
BUGFIXES:
bookkeeper-server:
BOOKKEEPER-567: ReadOnlyBookieTest hangs on shutdown (sijie via ivank)
BOOKKEEPER-549: Documentation missed for readOnlyMode support (ivank)
BOOKKEEPER-548: Document about periodic ledger checker configuration (ivank)
BOOKKEEPER-554: fd leaking when move ledger index file (sijie via ivank)
BOOKKEEPER-568: NPE during GC with HierarchicalLedgerManager (Matteo via sijie)
BOOKKEEPER-569: Critical performance bug in InterleavedLedgerStorage (ivank via fpj)
Release 4.2.0 - 2013-01-14
Non-backward compatible changes:
BUGFIXES:
IMPROVEMENTS:
bookkeeper-server:
BOOKKEEPER-203: improve ledger manager interface to remove zookeeper dependency on metadata operations. (sijie via ivank)
BOOKKEEPER-303: LedgerMetadata should serialized using protobufs (ivank)
hedwig-client:
BOOKKEEPER-339: Let hedwig cpp client support returning message seq id for publish requests. (sijie via ivank)
Backward compatible changes:
BUGFIXES:
BOOKKEEPER-289: mvn clean doesn't remove test output files (sijie via ivank)
BOOKKEEPER-298: We run with preferIPv4Stack in the scripts but not in the tests (ivank)
BOOKKEEPER-292: Test backward compatibility automatically between versions. (ivank)
BOOKKEEPER-352: Should not use static ServerStats/BKStats instance in TestServerStats/TestBKStats (sijie via fpj)
BOOKKEEPER-338: Create Version.NEW and Version.ANY static instances of Version so that were not passing around nulls (sijie via ivank)
BOOKKEEPER-32: Clean up LOG.debug statements (Stu Hood via sijie)
BOOKKEEPER-484: Misc fixes for test scripts (ivank via fpj)
BOOKKEEPER-483: precommit tests only check toplevel rat file, not the one for submodules. (ivank via fpj)
BOOKKEEPER-533: TestSubAfterCloseSub fails strangely in tests (ivank via fpj)
BOOKKEEPER-480: Fix javac warnings (ivank via sijie)
BOOKKEEPER-481: Fix javadoc warnings (ivank via sijie)
bookkeeper-server:
BOOKKEEPER-183: Provide tools to read/check data files in bookie server (sijie via ivank)
BOOKKEEPER-307: BookieShell introduces 4 findbugs warnings (ivank via sijie)
BOOKKEEPER-322: New protobufs generates findbugs errors (ivank)
BOOKKEEPER-280: LedgerHandle.addEntry() should return an entryId (mmerli via ivank)
BOOKKEEPER-324: Flakeyness in LedgerCreateDeleteTest (ivank)
BOOKKEEPER-318: Spelling mistake in MultiCallback log message. (surendra via sijie)
BOOKKEEPER-296: It's better provide stop script for bookie (nijel via sijie)
BOOKKEEPER-294: Not able to start the bookkeeper before the ZK session timeout. (rakeshr via ivank)
BOOKKEEPER-327: System.currentTimeMillis usage in BookKeeper (uma via fpj)
BOOKKEEPER-349: Entry logger should close all the chennels which are there in Map, instead of closing only current channel. (umamaheswararao via sijie)
BOOKKEEPER-326: DeadLock during ledger recovery (rakeshr via ivank)
BOOKKEEPER-372: Check service name in bookie start/stop script. (nijel via ivank)
BOOKKEEPER-354: [BOOKKEEPER-296] [Documentation] Modify the bookkeeper start script and document the bookkeeper stop command in bookkeeperConfig.xml (Kiran BC via ivank)
BOOKKEEPER-378: ReplicationWorker may not get ZK watcher notification on UnderReplication ledger lock deletion. (umamaheswararao & ivank via ivank)
BOOKKEEPER-380: ZkLedgerUnderreplicationManager.markLedgerUnderreplicated() is adding duplicate missingReplicas while multiple bk failed for the same ledger (rakeshr via ivank)
BOOKKEEPER-381: ReadLastConfirmedOp's Logger class name is wrong (surendra via sijie)
BOOKKEEPER-382: space missed at concatenations in GarbageCollectorThread logging (Brahma via sijie)
BOOKKEEPER-337: Add entry fails with MetadataVersionException when last ensemble has morethan one bookie failures (rakeshr via ivank)
BOOKKEEPER-376: LedgerManagers should consider 'underreplication' node as a special Znode (Uma via sijie)
BOOKKEEPER-384: Clean up LedgerManagerFactory and LedgerManager usage in tests (rakeshr via ivank)
BOOKKEEPER-385: replicateLedgerFragment should throw Exceptions in error conditions (umamahesh via ivank)
BOOKKEEPER-386: It should not be possible to replicate a ledger fragment which is at the end of an open ledger (ivank & umamahesh via ivank)
BOOKKEEPER-395: HDFS dep transitively depends on a busted pom (Stu Hood via sijie)
BOOKKEEPER-387: BookKeeper Upgrade is not working. (surendra via sijie)
BOOKKEEPER-383: NPE in BookieJournalTest (sijie via ivank)
BOOKKEEPER-396: Compilation issue in TestClient.java of BenchMark ( showing this in eclipse) (umamahesh via sijie)
BOOKKEEPER-403: ReReadMetadataCb is not executed in the thread responsible for that ledger (ivank)
BOOKKEEPER-405: Let's add Thread name for ReplicationWorker thread. (umamahesh via ivank)
BOOKKEEPER-418: Store hostname of locker in replication lock (ivank)
BOOKKEEPER-417: Hierarchical zk underreplication manager should clean up its hierarchy when done to allow for fast acquisition of underreplicated entries (ivank)
BOOKKEEPER-436: Journal#rollLog may leak file handler (umamahesh via ivank)
BOOKKEEPER-424: Bookie start is failing intermittently when zkclient connection delays (rakeshr via ivank)
BOOKKEEPER-416: LedgerChecker returns underreplicated fragments for an closed ledger with no entries (ivank)
BOOKKEEPER-425: Cleanup Bookie id generation (ivank via fpj)
BOOKKEEPER-430: Remove manual bookie registration from overview (fpj via ivank)
BOOKKEEPER-466: ZooKeeper test utility sets the port number as the tickTime (ivank)
BOOKKEEPER-460: LedgerDeleteTest checks wrong place for log file (Fangmin Lv via ivank)
BOOKKEEPER-477: In ReadOnlyBookieTest, we should wait for the bookie to die before asserting on it (ivank via fpj)
BOOKKEEPER-485: TestFencing hung (ivank via fpj)
BOOKKEEPER-351: asyncAddEntry should not throw an exception (Matteo Merli via sijie)
BOOKKEEPER-291: BKMBeanRegistry uses log4j directly (fpj via ivank)
BOOKKEEPER-459: Rename metastore mock implementation to InMemory implementation (jiannan via ivank)
BOOKKEEPER-347: Provide mechanism to detect r-o bookie by the bookie clients (Vinay via ivank)
BOOKKEEPER-475: BookieRecoveryTest#testSyncBookieRecoveryToRandomBookiesCheckForDupes() iterates too much (ivank via fpj)
BOOKKEEPER-431: Duplicate definition of COOKIES_NODE (uma via fpj)
BOOKKEEPER-474: BookieReadWriteTest#testShutdown doesn't make sense (ivank via fpj)
BOOKKEEPER-465: CreateNewLog may overwrite lastLogId with smaller value (yixue, fpj via fpj)
BOOKKEEPER-498: BookieRecoveryTest.tearDown NPE (fpj)
BOOKKEEPER-497: GcLedgersTest has a potential race (ivank via sijie)
BOOKKEEPER-493: moveLedgerIndexFile might have chance pickup same directory (sijie via ivank)
BOOKKEEPER-365: Ledger will never recover if one of the quorum bookie is down forever and others dont have entry (sijie via ivank)
BOOKKEEPER-336: bookie readEntries is taking more time if the ensemble has failed bookie(s) (ivank)
BOOKKEEPER-512: BookieZkExpireTest fails periodically (ivank via sijie)
BOOKKEEPER-509: TestBookKeeperPersistenceManager failed on latest trunk (sijie via ivank)
BOOKKEEPER-496: Ensure that the auditor and replication worker will shutdown if they lose their ZK session (ivank)
BOOKKEEPER-500: Fencing doesn't work when restarting bookies. (sijie via ivank)
BOOKKEEPER-520: BookieFailureTest hangs on precommit build (ivank via sijie)
BOOKKEEPER-447: Bookie can fail to recover if index pages flushed before ledger flush acknowledged (ivank via sijie)
BOOKKEEPER-520: BookieFailureTest hangs on precommit build (sijie via fpj, jira reopened)
BOOKKEEPER-514: TestDeadLock hanging sometimes (ivank, sijie via fpj)
BOOKKEEPER-524: Bookie journal filesystem gets full after SyncThread is terminated with exception (Matteo, fpj via sijie)
BOOKKEEPER-355: Ledger recovery will mark ledger as closed with -1, in case of slow bookie is added to ensemble during recovery add (ivank)
BOOKKEEPER-534: Flakeyness in AuditorBookieTest (umamahesh via ivank)
BOOKKEEPER-542: Remove trailing spaces in IndexCorruptionTest (fpj via ivank)
BOOKKEEPER-530: data might be lost during compaction. (ivank)
BOOKKEEPER-538: Race condition in BookKeeper#close (ivank via fpj)
BOOKKEEPER-408: BookieReadWriteTest will enter the endless loop and will not leave out (ivank)
BOOKKEEPER-504: Fix findbugs warning in PendingReadOp (fpj via ivank)
hedwig-protocol:
BOOKKEEPER-394: CompositeException message is not useful (Stu Hood via sijie)
BOOKKEEPER-468: Remove from protobuf generation in hedwig (ivank)
hedwig-client:
BOOKKEEPER-274: Hedwig cpp client library should not link to cppunit which is just used for test. (sijie via ivank)
BOOKKEEPER-320: Let hedwig cpp client could publish messages using Message object instead of string. (jiannan via ivank)
BOOKKEEPER-371: NPE in hedwig hub client causes hedwig hub to shut down. (Aniruddha via sijie)
BOOKKEEPER-392: Racey ConcurrentMap usage in java hedwig-client (Stu Hood via sijie)
BOOKKEEPER-427: TestConcurrentTopicAcquisition hangs every so often (ivank)
BOOKKEEPER-434: [Hedwig CPP Client] Delay resolving default host until necessary. (sijie via ivank)
BOOKKEEPER-452: Rename ClientConfiguration multiplexing_enabled to subscription_connection_sharing_enabled (sijie via ivank)
BOOKKEEPER-454: hedwig c++ tester script assumes sh is bash (ivank)
BOOKKEEPER-470: Possible infinite loop in simple.SubscribeReconnectCallback (sijie via ivank)
BOOKKEEPER-55: SubscribeReconnectRetryTask might retry subscription endlessly when another subscription is already successfully created previously (sijie via ivank)
BOOKKEEPER-513: TestMessageFilter fails periodically (ivank)
hedwig-server:
BOOKKEEPER-302: No more messages delivered when hub server scans messages over two ledgers. (sijie via ivank)
BOOKKEEPER-330: System.currentTimeMillis usage in Hedwig (uma via sijie)
BOOKKEEPER-343: Failed to register hedwig JMX beans in test cases (sijie via ivank)
BOOKKEEPER-259: Create a topic manager using versioned write for leader election (sijie via ivank)
BOOKKEEPER-191: Hub server should change ledger to write, so consumed messages have chance to be garbage collected. (sijie via ivank)
BOOKKEEPER-439: No more messages delivered after deleted consumed ledgers. (sijie via ivank)
BOOKKEEPER-440: Make Write/Delete SubscriptionData Restricted to Version (Fangmin Lv via ivank)
BOOKKEEPER-482: Precommit is reporting findbugs errors in trunk (ivank via sijie)
BOOKKEEPER-442: Failed to deliver messages due to inconsistency between SubscriptionState and LedgerRanges. (jiannan via ivank)
BOOKKEEPER-461: Delivery throughput degrades when there are lots of publishers w/ high traffic. (sijie via ivank)
BOOKKEEPER-458: Annoy BKReadException error when changing ledger. (jiannan via fpj)
BOOKKEEPER-507: Race condition happens if closeSubscription and subscribe happened at the same time (in multiplexed client). (sijie via ivank)
BOOKKEEPER-532: AbstractSubscriptionManager#AcquireOp read subscriptions every time even it already owned the topic. (sijie via fpj)
BOOKKEEPER-531: Cache thread should wait until old entries are collected (sijie via ivank)
BOOKKEEPER-529: stopServingSubscriber in delivery manager should remove stub callbacks in ReadAheadCache (sijie via ivank)
BOOKKEEPER-543: Read zk host list in a wrong way in hedwig server (Fangmin via sijie)
BOOKKEEPER-540: #stopServingSubscriber when channel is disconnected. (Fangmin via sijie)
BOOKKEEPER-539: ClientNotSubscribedException & doesn't receive enough messages in TestThrottlingDelivery#testServerSideThrottle (sijie)
BOOKKEEPER-503: The test case of TestThrottlingDelivery#testServerSideThrottle failed sometimes (jiannan & sijie via ivank)
IMPROVEMENTS:
BOOKKEEPER-467: Allocate ports for testing dynamically (ivank)
BOOKKEEPER-471: Add scripts for preCommit testing (ivank)
BOOKKEEPER-476: Log to file during tests (ivank via fpj)
BOOKKEEPER-491: Hedwig doc for configuration (fpj, sijie via fpj)
BOOKKEEPER-495: Revise BK config doc (fpj, ivank via fpj)
BOOKKEEPER-523: Every test should have a timeout (ivank, sijie via fpj)
BOOKKEEPER-541: Add guava to notice file (ivank via fpj)
bookkeeper-server:
BOOKKEEPER-328: Bookie DeathWatcher is missing thread name (Rakesh via sijie)
BOOKKEEPER-2: bookkeeper does not put enough meta-data in to do recovery properly (ivank via sijie)
BOOKKEEPER-317: Exceptions for replication (ivank via sijie)
BOOKKEEPER-246: Recording of underreplication of ledger entries (ivank)
BOOKKEEPER-247: Detection of under replication (ivank)
BOOKKEEPER-299: Provide LedgerFragmentReplicator which should replicate the fragments found from LedgerChecker (umamahesh via ivank)
BOOKKEEPER-248: Rereplicating of under replicated data (umamahesh via ivank)
BOOKKEEPER-304: Prepare bookie vs ledgers cache and will be used by the Auditor (rakeshr via ivank)
BOOKKEEPER-272: Provide automatic mechanism to know bookie failures (rakeshr via ivank)
BOOKKEEPER-300: Create Bookie format command (Vinay via sijie)
BOOKKEEPER-208: Separate write quorum from ack quorum (ivank)
BOOKKEEPER-325: Delay the replication of a ledger if RW found that its last fragment is in underReplication. (umamahesh via ivank)
BOOKKEEPER-388: Document bookie format command (kiran_bc via ivank)
BOOKKEEPER-278: Ability to disable auto recovery temporarily (rakeshr via ivank)
BOOKKEEPER-319: Manage auditing and replication processes (Vinay via ivank)
BOOKKEEPER-315: Ledger entries should be replicated sequentially instead of parallel. (umamahesh via ivank)
BOOKKEEPER-345: Detect IOExceptions on entrylogger and bookie should consider next ledger dir(if any) (Vinay via ivank)
BOOKKEEPER-346: Detect IOExceptions in LedgerCache and bookie should look at next ledger dir(if any) (Vinay via ivank)
BOOKKEEPER-444: Refactor pending read op to make speculative reads possible (ivank)
BOOKKEEPER-204: Provide a MetaStore interface, and a mock implementation. (Jiannan Wang via ivank)
BOOKKEEPER-469: Remove System.out.println from TestLedgerManager (ivank via fpj)
BOOKKEEPER-205: implement a MetaStore based ledger manager for bookkeeper client. (jiannan via ivank)
BOOKKEEPER-426: Make auditor Vote znode store a protobuf containing the host that voted (ivank)
BOOKKEEPER-428: Expose command options in bookie scripts to disable/enable auto recovery temporarily (rakesh,ivank via fpj)
BOOKKEEPER-511: BookieShell is very noisy (ivank via sijie)
BOOKKEEPER-375: Document about Auto replication service in BK (umamahesh via ivank)
BOOKKEEPER-490: add documentation for MetaStore interface (sijie, ivank via sijie)
BOOKKEEPER-463: Refactor garbage collection code for ease to plugin different GC algorithm. (Fangmin, ivank, fpj via sijie)
BOOKKEEPER-409: Integration Test - Perform bookie rereplication cycle by Auditor-RW processes (rakeshr via ivank)
BOOKKEEPER-293: Periodic checking of ledger replication status (ivank)
BOOKKEEPER-472: Provide an option to start Autorecovery along with Bookie Servers (umamahesh via ivank)
BOOKKEEPER-341: add documentation for bookkeeper ledger manager interface. (sijie via ivank)
hedwig-server:
BOOKKEEPER-250: Need a ledger manager like interface to manage metadata operations in Hedwig (sijie via ivank)
BOOKKEEPER-329: provide stop scripts for hub server (sijie via ivank)
BOOKKEEPER-331: Let hedwig support returning message seq id for publish requests. (Mridul via sijie)
BOOKKEEPER-340: Test backward compatibility for hedwig between different versions. (sijie via ivank)
BOOKKEEPER-283: Improve Hedwig Console to use Hedwig Metadata Manager. (sijie via ivank)
BOOKKEEPER-332: Add SubscriptionPreferences to record all preferences for a subscription (sijie via ivank)
BOOKKEEPER-333: server-side message filter (sijie via ivank)
BOOKKEEPER-252: Hedwig: provide a subscription mode to kill other subscription channel when hedwig client is used as a proxy-style server. (sijie via ivank)
BOOKKEEPER-397: Make the hedwig client in RegionManager configurable. (Aniruddha via sijie)
BOOKKEEPER-367: Server-Side Message Delivery Flow Control (sijie via ivank)
BOOKKEEPER-415: Rename DeliveryThrottle to MessageWindowSize (ivank via sijie)
BOOKKEEPER-422: Simplify AbstractSubscriptionManager (stu via fpj)
BOOKKEEPER-435: Create SubscriptionChannelManager to manage all subscription channel (sijie via ivank)
BOOKKEEPER-411: Add CloseSubscription Request for multiplexing support (sijie via ivank)
BOOKKEEPER-441: InMemorySubscriptionManager should back up top2sub2seq before change it (Yixue via ivank)
BOOKKEEPER-479: Fix apache-rat issues in tree (ivank via fpj)
BOOKKEEPER-457: Create a format command for Hedwig to cleanup its metadata. (sijie via ivank)
BOOKKEEPER-487: Add existed hub server settings to configuration template file (sijie via ivank)
BOOKKEEPER-389: add documentation for message filter. (sijie via ivank)
BOOKKEEPER-399: Let hub server configure write quorum from ack quorum. (sijie via fpj)
BOOKKEEPER-342: add documentation for hedwig metadata manager interface. (sijie, ivank via sijie)
BOOKKEEPER-522: TestHedwigHub is failing silently on Jenkins (ivank via sijie)
BOOKKEEPER-262: Implement a meta store based hedwig metadata manager. (jiannan via ivank)
BOOKKEEPER-310: Changes in hedwig server to support JMS spec (ivank via sijie)
hedwig-client:
BOOKKEEPER-306: Change C++ client to use gtest for testing (ivank via sijie)
BOOKKEEPER-334: client-side message filter for java client. (sijie via ivank)
BOOKKEEPER-335: client-side message filter for cpp client. (sijie via ivank)
BOOKKEEPER-364: re-factor hedwig java client to support both one-subscription-per-channel and multiplex-subscriptions-per-channel. (sijie via ivank)
BOOKKEEPER-143: Add SSL support for hedwig cpp client (sijie via ivank)
BOOKKEEPER-413: Hedwig C++ client: Rename RUN_AS_SSL_MODE to SSL_ENABLED (ivank via sijie)
BOOKKEEPER-369: re-factor hedwig cpp client to provide better interface to support both one-subscription-per-channel and multiple-subscriptions-per-channel. (sijie via ivank)
BOOKKEEPER-368: Implementing multiplexing java client. (sijie via ivank)
BOOKKEEPER-370: implement multiplexing cpp client. (sijie via ivank)
BOOKKEEPER-453: Extract commonality from MultiplexSubscribeResponseHandler and SimpleSubscribeResponseHandler and put into an abstract class (sijie via ivank)
BOOKKEEPER-404: Deprecate non-SubscriptionOptions Subscriber Apis (ivank via sijie)
Release 4.1.0 - 2012-06-07
Non-backward compatible changes:
BUGFIXES:
IMPROVEMENTS:
Backward compatible changes:
BUGFIXES:
BOOKKEEPER-145: Put notice and license file for distributed binaries in SVN (ivank)
BOOKKEEPER-254: Bump zookeeper version in poms (ivank)
BOOKKEEPER-72: Fix warnings issued by FindBugs (ivank)
BOOKKEEPER-238: Add log4j.properties in conf/ for bin packages (ivank)
bookkeeper-server/
BOOKKEEPER-142: Parsing last log id is wrong, which may make entry log files overwritten (Sijie Gou via ivank)
BOOKKEEPER-141: Run extracting ledger id from entry log files in GC thread to speed up bookie restart (Sijie Gou via ivank)
BOOKKEEPER-148: Jenkins build is failing (ivank via fpj)
BOOKKEEPER-40: BookieClientTest fails intermittantly (fpj via ivank)
BOOKKEEPER-150: Entry is lost when recovering a ledger with not enough bookies. (Sijie Guo via ivank)
BOOKKEEPER-153: Ledger can't be opened or closed due to zero-length metadata (Sijie Guo via ivank)
BOOKKEEPER-23: Timeout requests (ivank)
BOOKKEEPER-161: PerChannelBookieClient tries to reuse HashedWheelTimer, throws Exception (ivank)
BOOKKEEPER-167: PerChannelBookieClient doesn't use ClientConfiguration (Sijie Guo via ivank)
BOOKKEEPER-156: BookieJournalRollingTest failing (Sijie Guo via ivank)
BOOKKEEPER-162: LedgerHandle.readLastConfirmed does not work (fpj)
BOOKKEEPER-152: Can't recover a ledger whose current ensemble contain failed bookie. (ivank)
BOOKKEEPER-171: ServerConfiguration can't use more than one directory for ledgers. (ivank via sijie)
BOOKKEEPER-170: Bookie constructor starts a number of threads. (ivank via fpj)
BOOKKEEPER-169: bookie hangs on reading header when encountering partial header index file (sijie via ivank)
BOOKKEEPER-174: Bookie can't start when replaying entries whose ledger were deleted and garbage collected. (sijie via ivank)
BOOKKEEPER-177: Index file is lost or some index pages aren't flushed. (sijie via ivank)
BOOKKEEPER-113: NPE In BookKeeper test (fpj via ivank)
BOOKKEEPER-176: HierarchicalBookieFailureTest Hung (ivank via fpj)
BOOKKEEPER-180: bookie server doesn't quit when running out of disk space (sijie via ivank)
BOOKKEEPER-185: Remove bookkeeper-server dependency on hadoop-common (ivank)
BOOKKEEPER-184: CompactionTest failing on Jenkins (sijie via ivank)
BOOKKEEPER-182: Entry log file is overwritten when fail to read lastLogId. (sijie via ivank)
BOOKKEEPER-186: Bookkeeper throttling - permits is not released when read has failed from all replicas (Rakesh R via sijie)
BOOKKEEPER-189: AbstractZkLedgerManager doesn't disregard cookies (ivank via sijie)
BOOKKEEPER-195: HierarchicalLedgerManager doesn't consider idgen as a "specialNode" (ivank)
BOOKKEEPER-190: Add entries would fail when number of open ledgers reaches more than openFileLimit. (sijie via ivank)
BOOKKEEPER-194: Get correct latency for addEntry operations for JMX. (sijie via ivank)
BOOKKEEPER-166: Bookie will not recover its journal if the length prefix of an entry is truncated (ivank)
BOOKKEEPER-193: Ledger is garbage collected by mistake. (sijie, ivank via sijie)
BOOKKEEPER-198: replaying entries of deleted ledgers would exhaust ledger cache. (sijie)
BOOKKEEPER-112: Bookie Recovery on an open ledger will cause LedgerHandle#close on that ledger to fail (sijie)
BOOKKEEPER-135: Fencing does not check the ledger masterPasswd (ivank)
BOOKKEEPER-212: Bookie stops responding when creating and deleting many ledgers (sijie via fpj)
BOOKKEEPER-211: Bookie fails to to start (sijie)
BOOKKEEPER-200: Fix format and comments (fpj)
BOOKKEEPER-216: Bookie doesn't exit with right exit code (sijie via ivank)
BOOKKEEPER-196: Define interface between bookie and ledger storage (ivank)
BOOKKEEPER-213: PerChannelBookieClient calls the wrong errorOut function when encountering an exception (Aniruddha via sijie)
BOOKKEEPER-231: ZKUtil.killServer not closing the FileTxnSnapLog from ZK. (Uma Maheswara Rao G via sijie)
BOOKKEEPER-232: AsyncBK tests failing (umamaheswararao via ivank)
BOOKKEEPER-229: Deleted entry log files would be garbage collected again and again. (sijie via fpj)
BOOKKEEPER-242: Bookkeeper not able to connect other zookeeper when shutdown the zookeeper server where the BK has connected. (sijie & rakeshr via ivank)
BOOKKEEPER-234: EntryLogger will throw NPE, if any dir does not exist or IO Errors. (umamaheswararao via ivank)
BOOKKEEPER-235: Bad syncing in entrylogger degrades performance for many concurrent ledgers (ivank via fpj)
BOOKKEEPER-224: Fix findbugs in bookkeeper-server component (ivank)
BOOKKEEPER-251: Noise error message printed when scanning entry log files those have been garbage collected. (sijie via ivank)
BOOKKEEPER-266: Review versioning documentation (ivank)
BOOKKEEPER-258: CompactionTest failed (ivank via sijie)
BOOKKEEPER-273: LedgerHandle.deleteLedger() should be idempotent (Matteo Merli via ivank)
BOOKKEEPER-281: BKClient is failing when zkclient connection delays (ivank via sijie)
BOOKKEEPER-279: LocalBookKeeper is failing intermittently due to zkclient connection establishment delay (Rakesh R via sijie)
BOOKKEEPER-286: Compilation warning (ivank via sijie)
BOOKKEEPER-287: NoSuchElementException in LedgerCacheImpl (sijie)
BOOKKEEPER-288: NOTICE files don't have the correct year (ivank via sijie)
hedwig-client/
BOOKKEEPER-217: NPE in hedwig client when enable DEBUG (sijie via ivank)
hedwig-server/
BOOKKEEPER-140: Hub server doesn't subscribe remote region correctly when a region is down. (Sijie Gou via ivank)
BOOKKEEPER-133: Hub server should update subscription state to zookeeper when losing topic or shutting down (Sijie Gou via ivank)
BOOKKEEPER-74: Bookkeeper Persistence Manager should give up topic on error (sijie via ivank)
BOOKKEEPER-163: Prevent incorrect NoSuchLedgerException for readLastConfirmed. (ivank via sijie)
BOOKKEEPER-197: HedwigConsole uses the same file to load bookkeeper client config and hub server config (sijie)
BOOKKEEPER-56: Race condition of message handler in connection recovery in Hedwig client (sijie & Gavin Li via ivank)
BOOKKEEPER-215: Deadlock occurs under high load (sijie via ivank)
BOOKKEEPER-245: Intermittent failures in PersistanceManager tests (ivank)
BOOKKEEPER-209: Typo in ServerConfiguration for READAHEAD_ENABLED (ivank)
BOOKKEEPER-146: TestConcurrentTopicAcquisition sometimes hangs (ivank)
BOOKKEEPER-285: TestZkSubscriptionManager quits due to NPE, so other tests are not run in hedwig server. (sijie)
bookkeeper-benchmark/
BOOKKEEPER-207: BenchBookie doesn't run correctly (ivank via fpj)
BOOKKEEPER-228: Fix the bugs in BK benchmark (umamaheswararao via ivank)
IMPROVEMENTS:
BOOKKEEPER-265: Review JMX documentation (sijie via fpj)
bookkeeper-server/
BOOKKEEPER-95: extends zookeeper JMX to monitor and manage bookie server (Sijie Guo via ivank)
BOOKKEEPER-98: collect add/read statistics on bookie server (Sijie Guo via ivank)
BOOKKEEPER-157: For small packets, increasing number of bookies actually degrades performance. (ivank via fpj)
BOOKKEEPER-165: Add versioning support for journal files (ivank)
BOOKKEEPER-137: Do not create Ledger index files until absolutely necessary. (ivank)
BOOKKEEPER-172: Upgrade framework for filesystem layouts (ivank via fpj)
BOOKKEEPER-178: Delay ledger directory creation until the ledger index file was created (sijie via ivank)
BOOKKEEPER-160: bookie server needs to do compaction over entry log files to reclaim disk space (sijie via ivank)
BOOKKEEPER-187: Create well defined interface for LedgerCache (ivank)
BOOKKEEPER-175: Bookie code is very coupled (ivank)
BOOKKEEPER-188: Garbage collection code is in the wrong place (ivank via sijie)
BOOKKEEPER-218: Provide journal manager to manage journal related operations (sijie)
BOOKKEEPER-173: Uncontrolled number of threads in bookkeeper (sijie via fpj)
BOOKKEEPER-241: Add documentation for bookie entry log compaction (sijie via fpj)
BOOKKEEPER-263: ZK ledgers root path is hard coded (Aniruddha via sijie)
BOOKKEEPER-260: Define constant for -1 (invalid entry id) (ivank via fpj)
BOOKKEEPER-270: Review documentation on bookie cookie (ivank via fpj)
hedwig-server/
BOOKKEEPER-77: Add a console client for hedwig (Sijie Guo via ivank)
BOOKKEEPER-168: Message bounding on subscriptions (ivank)
BOOKKEEPER-96: extends zookeeper JMX to monitor and manage hedwig server (sijie via ivank)
BOOKKEEPER-97: collect pub/sub/consume statistics on hub server (sijie via ivank)
BOOKKEEPER-269: Review documentation for hedwig console client (sijie via fpj)
hedwig-client/
BOOKKEEPER-271: Review documentation for message bounding (ivank via fpj)
bookkeeper-benchmark/
BOOKKEEPER-158: Move latest benchmarking code into trunk (ivank via fpj)
BOOKKEEPER-236: Benchmarking improvements from latest round of benchmarking (ivank via fpj)
Release 4.0.0 - 2011-11-30
Non-backward compatible changes:
BUGFIXES:
BOOKKEEPER-89: Bookkeeper API changes for initial Bookkeeper release (ivank)
BOOKKEEPER-108: add configuration support for BK (Sijie via ivank)
BOOKKEEPER-90: Hedwig API changes for initial Bookkeeper release (ivank via fpj)
Backward compatible changes:
BUGFIXES:
BOOKKEEPER-124: build has RAT failures (ivank)
BOOKKEEPER-121: Review Hedwig client documentation (breed via ivank)
BOOKKEEPER-127: Make poms use official zookeeper 3.4.0 (ivank)
BOOKKEEPER-120: Review BookKeeper client documentation (ivank)
BOOKKEEPER-122: Review BookKeeper server documentation (fpj & ivank)
BOOKKEEPER-66: use IPv4 for builds (mmorel via ivank)
BOOKKEEPER-132: Sign artifacts before deploying to maven (ivank)
BOOKKEEPER-131: Fix zookeeper test dependency (ivank)
BOOKKEEPER-134: Delete superfluous lib directories (ivank)
BOOKKEEPER-138: NOTICE.txt is invalid (ivank)
BOOKKEEPER-139: Binary packages do not carry NOTICE.txt (ivank)
bookkeeper-server/
BOOKKEEPER-1: Static variable makes tests fail (fpj via ivank)
BOOKKEEPER-19: BookKeeper doesn't support more than 2Gig of memory (ivan via fpj)
BOOKEEPER-22: Exception in LedgerCache causes addEntry request to fail (fpj via fpj)
BOOKEEPER-5: Issue with Netty in BookKeeper (fpj and ivank via fpj)
BOOKKEEPER-30: Test are too noisy (ivank via fpj)
BOOKKEEPER-11: Read from open ledger (fpj via ivank)
BOOKKEEPER-27: mvn site failed with unresolved dependencies (ivank via fpj)
BOOKKEEPER-29: BookieRecoveryTest fails intermittently (fpj via ivank)
BOOKKEEPER-33: Add length and offset parameter to addEntry (ivank via fpj)
BOOKKEEPER-29: BookieRecoveryTest fails intermittently (ivank, fpj via fpj)
BOOKKEEPER-38: Bookie Server doesn't exit when its zookeeper session is expired. So the process is hang there. (Sijie Guo via breed)
BOOKKEEPER-58: Changes introduced in BK-38 cause BookieClientTest to hang indefinitely. (ivank)
BOOKKEEPER-18: maven build is unstable (mmorel, ivank via ivank)
BOOKKEEPER-57: NullPointException at bookie.zk@EntryLogger (xulei via ivank)
BOOKKEEPER-59: Race condition in netty code allocates and orphans resources (BK-5 revisited) (ivank via fpj)
BOOKKEEPER-68: Conditional setData (fpj via ivank)
BOOKKEEPER-86: bookkeeper-benchmark fails to compile after BOOKKEEPER-68 (ivank via breed)
BOOKKEEPER-61: BufferedChannel read endless when the remaining bytes of file is less than the capacity of read buffer (Sijie Guo via breed)
BOOKKEEPER-84: Add versioning for ZK metadata (ivank via breed)
BOOKKEEPER-92: using wrong context object in readLastConfirmedComplete callback (Sijie Guo via ivank)
BOOKKEEPER-94: Double callbacks in readLastConfirmedOp which fails readLastConfirmed operation even received enough valid responses. (Sijie Guo via ivank)
BOOKKEEPER-83: Added versioning and flags to the bookie protocol (ivank)
BOOKKEEPER-93: bookkeeper doesn't work correctly on OpenLedgerNoRecovery (Sijie Guo via ivank)
BOOKKEEPER-103: ledgerId and entryId is parsed wrong when addEntry (Sijie Guo via ivank)
BOOKKEEPER-50: NullPointException at LedgerDescriptor#cmpMasterKey (Sijie Guo via ivank)
BOOKKEEPER-82: support journal rolling (Sijie Guo via fpj)
BOOKKEEPER-106: recoveryBookieData can select a recovery bookie which is already in the ledgers ensemble (ivank via fpj)
BOOKKEEPER-101: Add Fencing to Bookkeeper (ivank)
BOOKKEEPER-104: Add versioning between bookie and its filesystem layout (ivank)
BOOKKEEPER-81: disk space of garbage collected entry logger files isn't reclaimed util process quit (Sijie Guo via fpj)
BOOKKEEPER-91: Bookkeeper and hedwig clients should not use log4j directly (ivank via fpj)
BOOKKEEPER-115: LocalBookKeeper fails after BOOKKEEPER-108 (ivank)
BOOKKEEPER-114: add a shutdown hook to shut down bookie server safely. (Sijie via ivank)
BOOKKEEPER-39: Bookie server failed to restart because of too many ledgers (more than ~50,000 ledgers) (Sijie via ivank)
BOOKKEEPER-125: log4j still used in some places (ivank)
BOOKKEEPER-62: Bookie can not start when encountering corrupted records (breed via ivank)
BOOKKEEPER-111: Document bookie recovery feature (ivank)
BOOKKEEPER-129: ZK_TIMEOUT typo in client/server configuration (Sijie via ivank)
BOOKKEEPER-22: Exception in LedgerCache causes addEntry request to fail (fpj via fpj)
BOOKKEEPER-5: Issue with Netty in BookKeeper (fpj and ivank via fpj)
hedwig-server/
BOOKKEEPER-43: NullPointException when releasing topic (Sijie Guo via breed)
BOOKKEEPER-51: NullPointException at FIFODeliveryManager#deliveryPtrs (xulei via ivank)
BOOKKEEPER-63: Hedwig PubSubServer must wait for its Zookeeper client to be connected upon startup (mmorel via ivank)
BOOKKEEPER-100: Some hedwig tests have build errors (dferro via ivank)
BOOKKEEPER-69: ServerRedirectLoopException when a machine (hosts bookie server & hub server) reboot, which is caused by race condition of topic manager (Sijie, ivank via ivank)
hedwig-client/
BOOKKEEPER-52: Message sequence confuse due to the subscribeMsgQueue@SubscribeResponseHandler (xulei via ivank)
BOOKKEEPER-88: derby doesn't like - in the topic names (breed via ivank)
BOOKKEEPER-71: hedwig c++ client does not build . (ivank)
BOOKKEEPER-107: memory leak in HostAddress of hedwig c++ client (Sijie Guo via ivank)
BOOKKEEPER-80: subscription msg queue race condition in hedwig c++ client (Sijie Guo via ivank)
BOOKKEEPER-87: TestHedwigHub exhausts direct buffer memory with netty 3.2.4.Final (ivank via fpj)
BOOKKEEPER-79: randomly startDelivery/stopDelivery will core dump in c++ hedwig client (Sijie Guo via ivank)
BOOKKEEPER-118: Hedwig client doesn't kill and remove old subscription channel after redirection. (Sijie Guo via ivank)
BOOKKEEPER-117: Support multi threads in hedwig cpp client to leverage multi-core hardware (Sijie Guo via ivank)
BOOKKEEPER-53: race condition of outstandingMsgSet@SubscribeResponseHandler (fpj via breed)
IMPROVEMENTS:
BOOKKEEPER-28: Create useful startup scripts for bookkeeper and hedwig (ivank)
BOOKKEEPER-26: Indentation is all messed up in the BookKeeper code (ivank via fpj)
BOOKKEEPER-41: Generation of packages for distribution (ivank via fpj)
BOOKKEEPER-65: fix dependencies on incompatible versions of netty (mmorel via ivank)
BOOKKEEPER-102: Make bookkeeper use ZK from temporary repo (ivank)
BOOKKEEPER-128: pom and script modifications required for generating release packages (ivank)
hedwig-client/
BOOKKEEPER-44: Reuse publish channel to default server to avoid too many connect requests to default server when lots of producers came in same time (Sijie Guo via breed)
BOOKKEEPER-109: Add documentation to describe how bookies flushes data (Sijie Guo via fpj)
BOOKKEEPER-119: Keys in configuration have inconsistent style (ivank via fpj)
bookkeeper-release-4.2.4/LICENSE 0000664 0000000 0000000 00000026136 12445073612 0016306 0 ustar 00root root 0000000 0000000
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed 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.
bookkeeper-release-4.2.4/NOTICE 0000664 0000000 0000000 00000000254 12445073612 0016176 0 ustar 00root root 0000000 0000000 Apache BookKeeper
Copyright 2011-2014 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
bookkeeper-release-4.2.4/README 0000664 0000000 0000000 00000006454 12445073612 0016162 0 ustar 00root root 0000000 0000000 Build instructions for BookKeeper
-------------------------------------------------------------------------------
Requirements:
* Unix System
* JDK 1.6
* Maven 3.0
* Autotools (if compiling native hedwig client)
* Internet connection for first build (to fetch all dependencies)
-------------------------------------------------------------------------------
The BookKeeper project contains:
- bookkeeper-server (BookKeeper server and client)
- bookkeeper-benchmark (Benchmark suite for testing BookKeeper performance)
- hedwig-protocol (Hedwig network protocol)
- hedwig-client (Hedwig client library)
- hedwig-server (Hedwig server)
BookKeeper is a system to reliably log streams of records. It is designed to
store write ahead logs, such as those found in database or database like
applications.
Hedwig is a publish-subscribe system designed to carry large amounts of data
across the internet in a guaranteed-delivery fashion from those who produce
it (publishers) to those who are interested in it (subscribers).
--------------------------------------------------------------------------------
How do I build?
BookKeeper uses maven as its build system. To build, run "mvn package" from the
top-level directory, or from within any of the submodules.
Useful maven commands are:
* Clean : mvn clean
* Compile : mvn compile
* Run tests : mvn test
* Create JAR : mvn package
* Run findbugs : mvn compile findbugs:findbugs
* Install JAR in M2 cache : mvn install
* Deploy JAR to Maven repo : mvn deploy
* Run Rat : mvn apache-rat:check
* Build javadocs : mvn compile javadoc:aggregate
* Build distribution : mvn package assembly:single
Tests options:
* Use -DskipTests to skip tests when running the following Maven goals:
'package', 'install', 'deploy' or 'verify'
* -Dtest=,,....
* -Dtest.exclude=
* -Dtest.exclude.pattern=**/.java,**/.java
--------------------------------------------------------------------------------
How do I run the services?
Running a Hedwig service requires a running BookKeeper service, which in turn
requires a running ZooKeeper service (see http://zookeeper.apache.org). To
start a bookkeeper service quickly for testing, run:
$ bookkeeper-server/bin/bookkeeper localbookie 10
This will start a standalone, ZooKeeper instance and 10 BookKeeper bookies.
Note that this is only useful for testing. Data is not persisted between runs.
To start a real BookKeeper service, you must set up a ZooKeeper instance and
run start a bookie on several machines. Modify bookkeeper-server/conf/bk_server.conf
to point to your ZooKeeper instance. To start a bookie run:
$ bookkeeper-server/bin/bookkeeper bookie
Once you have at least 3 bookies runnings, you can start some Hedwig hubs. A
hub is a machines which is responsible for a set of topics in the pubsub
system. The service automatically distributes the topics among the hubs.
To start a hedwig hub:
$ hedwig-server/bin/hedwig server
You can get more help on using these commands by running:
$ bookkeeper-server/bin/bookkeeper help
and
$ hedwig-server/bin/hedwig help
bookkeeper-release-4.2.4/bin/ 0000775 0000000 0000000 00000000000 12445073612 0016041 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bin/find-new-patch-available-jiras 0000775 0000000 0000000 00000010510 12445073612 0023614 0 ustar 00root root 0000000 0000000 #!/bin/bash
#
# Licensed 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.
if [ "${TESTPATCHDEBUG}" == "true" ] ; then
set -x
fi
BASEDIR=$(pwd)
TEMPDIR=${BASEDIR}/tmp
JIRAAVAILPATCHQUERY="https://issues.apache.org/jira/sr/jira.issueviews:searchrequest-xml/temp/SearchRequest.xml?jqlQuery=project+in+%28BOOKKEEPER%29+AND+status+%3D+%22Patch+Available%22+ORDER+BY+updated+DESC&tempMax=1000"
TESTPATCHJOBURL="https://builds.apache.org/job/bookkeeper-trunk-precommit-build"
TOKEN=""
SUBMIT="false"
DELETEHISTORYFILE="false"
RUNTESTSFILE=${BASEDIR}/TESTED_PATCHES.txt
printUsage() {
echo "Usage: $0 "
echo " --submit --token="
echo " [--delete-history-file]"
echo " [--script-debug]"
echo
}
###############################################################################
parseArgs() {
for i in $*
do
case $i in
--submit)
SUBMIT="true"
;;
--token=*)
TOKEN=${i#*=}
;;
--script-debug)
DEBUG="-x"
;;
--delete-history-file)
DELETEHISTORYFILE="true"
;;
*)
echo "Invalid option"
echo
printUsage
exit 1
;;
esac
done
if [[ "$SUBMIT" == "true" && "${TOKEN}" == "" ]] ; then
echo "Token has not been specified"
echo
printUsage
exit 1
fi
}
###############################################################################
findAndSubmitAvailablePatches() {
## Grab all the key (issue numbers) and largest attachment id for each item in the XML
curl --fail --location --retry 3 "${JIRAAVAILPATCHQUERY}" > ${TEMPDIR}/patch-availables.xml
if [ "$?" != "0" ] ; then
echo "Could not retrieve available patches from JIRA"
exit 1
fi
xpath -e "//item/key/text() | //item/attachments/attachment[not(../attachment/@id > @id)]/@id" \
${TEMPDIR}/patch-availables.xml > ${TEMPDIR}/patch-attachments.element
### Replace newlines with nothing, then replace id=" with =, then replace " with newline
### to yield lines with pairs (issueNumber,largestAttachmentId). Example: BOOKKEEPER-123,456984
cat ${TEMPDIR}/patch-attachments.element \
| awk '{ if ( $1 ~ /^BOOKKEEPER\-/) {JIRA=$1 }; if ($1 ~ /id=/) { print JIRA","$1} }' \
| sed 's/id\="//' | sed 's/"//' > ${TEMPDIR}/patch-availables.pair
### Iterate through issue list and find the (issueNumber,largestAttachmentId) pairs that have
### not been tested (ie don't already exist in the patch_tested.txt file
touch ${RUNTESTSFILE}
cat ${TEMPDIR}/patch-availables.pair | while read PAIR ; do
set +e
COUNT=`grep -c "$PAIR" ${RUNTESTSFILE}`
set -e
if [ "$COUNT" -lt "1" ] ; then
### Parse $PAIR into project, issue number, and attachment id
ISSUE=`echo $PAIR | sed -e "s/,.*$//"`
echo "Found new patch for issue $ISSUE"
if [ "$SUBMIT" == "true" ]; then
### Kick off job
echo "Submitting job for issue $ISSUE"
curl --fail --location --retry 3 \
"${TESTPATCHJOBURL}/buildWithParameters?token=${TOKEN}&JIRA_NUMBER=${ISSUE}" > /dev/null
if [ "$?" != "0" ] ; then
echo "Could not submit precommit job for $ISSUE"
exit 1
fi
fi
### Mark this pair as tested by appending to file
echo "$PAIR" >> ${RUNTESTSFILE}
fi
done
}
###############################################################################
mkdir -p ${TEMPDIR} 2>&1 $STDOUT
parseArgs "$@"
if [ -n "${DEBUG}" ] ; then
set -x
fi
if [ "${DELETEHISTORYFILE}" == "true" ] ; then
rm ${RUNTESTSFILE}
fi
findAndSubmitAvailablePatches
exit 0
bookkeeper-release-4.2.4/bin/raw-check-patch 0000664 0000000 0000000 00000002406 12445073612 0020727 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
#
# Licensed 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.
printTrailingSpaces() {
PATCH=$1
cat $PATCH | awk '/^+/ { if (/ $/) { print "\tL" NR ":" $0} }'
}
printTabs() {
PATCH=$1
cat $PATCH | awk '/^+/ { if (/\t/) { print "\tL" NR ":" $0 } }'
}
printAuthors() {
PATCH=$1
cat $PATCH | awk '/^+/ { L=tolower($0); if (L ~ /.*\*.* @author/) { print "\tL" NR ":" $0 } }'
}
printLongLines() {
PATCH=$1
cat $PATCH | awk '/^+/ { if ( length > 121 ) { print "\tL" NR ":" $0 } }'
}
if [[ "X$(basename -- "$0")" = "Xraw-check-patch" ]]; then
echo Trailing spaces
printTrailingSpaces $1
echo
echo Tabs
printTabs $1
echo
echo Authors
printAuthors $1
echo
echo Long lines
printLongLines $1
fi
bookkeeper-release-4.2.4/bin/test-patch 0000775 0000000 0000000 00000031204 12445073612 0020043 0 ustar 00root root 0000000 0000000 #!/bin/bash
#
# Licensed 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.
if [ "${TESTPATCHDEBUG}" == "true" ] ; then
set -x
fi
BASEDIR=$(pwd)
TESTPATCHDIRNAME=test-patch
TESTPATCHDIR=${BASEDIR}/${TESTPATCHDIRNAME}
TOOLSDIR=${TESTPATCHDIR}/tools
TEMPDIR=${TESTPATCHDIR}/tmp
REPORTDIR=${TESTPATCHDIR}/reports
SUMMARYFILE=${REPORTDIR}/TEST-SUMMARY.jira
SUMMARYFILETXT=${REPORTDIR}/TEST-SUMMARY.txt
JIRAHOST="https://issues.apache.org"
JIRAURL="${JIRAHOST}/jira"
JIRAURLISSUEPREFIX="${JIRAURL}/browse/"
JIRAUPDATE="false"
JIRAUSER=""
JIRAPASSWORD=""
VERBOSEOPTION=""
JIRAISSUE=""
PATCHFILE=""
TASKSTORUN=""
TASKSTOSKIP=""
RESETSCM="false"
DIRTYSCM="false"
STDOUT="/dev/null"
MVNPASSTHRU=""
###############################################################################
gitOrSvn() {
SCM="NONE"
which git &> /dev/null
if [[ $? == 0 ]] ; then
git status &> /dev/null
if [[ $? == 0 ]] ; then
SCM="git"
fi
fi
if [ "${SCM}" == "NONE" ] ; then
which svn &> /dev/null
if [[ $? == 0 ]] ; then
svnOutput=`svn status 2>&1`
if [[ "$svnOutput" != *"is not a working copy" ]] ; then
SCM="svn"
fi
fi
fi
if [ "${SCM}" == "NONE" ] ; then
echo "The current workspace is not under Source Control (GIT or SVN)"
exit 1
fi
}
###############################################################################
prepareSCM() {
gitOrSvn
if [ "${DIRTYSCM}" != "true" ] ; then
if [ "${RESETSCM}" == "true" ] ; then
if [ "${SCM}" == "git" ] ; then
git reset --hard HEAD > /dev/null
git clean -f -d -e $TESTPATCHDIRNAME > /dev/null
fi
if [ "${SCM}" == "svn" ] ; then
svn revert -R . > /dev/null
svn status | grep "\?" | awk '{print $2}' | xargs rm -rf
fi
else
echo "It should not happen DIRTYSCM=false & RESETSCM=false"
exit 1
fi
echo "Cleaning local ${SCM} workspace" >> ${SUMMARYFILE}
else
echo "WARNING: Running test-patch on a dirty local ${SCM} workspace" >> ${SUMMARYFILE}
fi
}
###############################################################################
prepareTestPatchDirs() {
mkdir -p ${TESTPATCHDIR} 2> /dev/null
rm -rf ${REPORTDIR} 2> /dev/null
rm -rf ${TEMPDIR} 2> /dev/null
mkdir -p ${TOOLSDIR} 2> /dev/null
mkdir -p ${TEMPDIR} 2> /dev/null
mkdir -p ${REPORTDIR} 2> /dev/null
if [ ! -e "${TESTPATCHDIR}" ] ; then
echo "Could not create test-patch/ dir"
exit 1
fi
}
###############################################################################
updateJira() {
if [[ "${JIRAUPDATE}" != "" && "${JIRAISSUE}" != "" ]] ; then
if [[ "$JIRAPASSWORD" != "" ]] ; then
JIRACLI=${TOOLSDIR}/jira-cli/jira.sh
if [ ! -e "${JIRACLI}" ] ; then
curl https://bobswift.atlassian.net/wiki/download/attachments/16285777/jira-cli-2.6.0-distribution.zip > ${TEMPDIR}/jira-cli.zip
if [ $? != 0 ] ; then
echo
echo "Could not download jira-cli tool, thus no JIRA updating"
echo
exit 1
fi
mkdir ${TEMPDIR}/jira-cli-tmp
(cd ${TEMPDIR}/jira-cli-tmp;jar xf ${TEMPDIR}/jira-cli.zip; mv jira-cli-2.6.0 ${TOOLSDIR}/jira-cli)
chmod u+x ${JIRACLI}
fi
echo "Adding comment to JIRA"
comment=`cat ${SUMMARYFILE}`
$JIRACLI -s $JIRAURL -a addcomment -u $JIRAUSER -p "$JIRAPASSWORD" --comment "$comment" --issue $JIRAISSUE
echo
else
echo "Skipping JIRA update"
echo
fi
fi
}
###############################################################################
cleanupAndExit() {
updateJira
exit $1
}
###############################################################################
printUsage() {
echo "Usage: $0 "
echo " (--jira= | --patch=)"
echo " (--reset-scm | --dirty-scm)"
echo " [--tasks=]"
echo " [--skip-tasks=]"
echo " [--jira-cli=]"
echo " [--jira-user=]"
echo " [--jira-password=]"
echo " [-D...]"
echo " [-P...]"
echo " [--list-tasks]"
echo " [--verbose]"
echo
}
###############################################################################
parseArgs() {
for i in $*
do
case $i in
--jira=*)
JIRAISSUE=${i#*=}
;;
--patch=*)
PATCHFILE=${i#*=}
;;
--tasks=*)
TASKSTORUN=${i#*=}
;;
--skip-tasks=*)
TASKSTOSKIP=${i#*=}
;;
--list-tasks)
listTasks
cleanupAndExit 0
;;
--jira-cli=*)
JIRACLI=${i#*=}
;;
--jira-user=*)
JIRAUSER=${i#*=}
;;
--jira-password=*)
JIRAPASSWORD=${i#*=}
JIRAUPDATE="true"
;;
-D*)
MVNPASSTHRU="${MVNPASSTHRU} $i"
;;
-P*)
MVNPASSTHRU="${MVNPASSTHRU} $i"
;;
--reset-scm)
RESETSCM="true"
;;
--dirty-scm)
DIRTYSCM="true"
;;
--verbose)
VERBOSEOPTION="--verbose"
STDOUT="/dev/stdout"
;;
*)
echo "Invalid option"
echo
printUsage
exit 1
;;
esac
done
if [[ "${JIRAISSUE}" == "" && "${PATCHFILE}" == "" ]] ; then
echo "Either --jira or --patch option must be specified"
echo
printUsage
exit 1
fi
if [[ "${JIRAISSUE}" != "" && "${PATCHFILE}" != "" ]] ; then
echo "Cannot specify --jira or --patch options together"
echo
printUsage
exit 1
fi
if [[ "${RESETSCM}" == "false" && "${DIRTYSCM}" == "false" ]] ; then
echo "Either --reset-scm or --dirty-scm option must be specified"
echo
printUsage
exit 1
fi
if [[ "${RESETSCM}" == "true" && "${DIRTYSCM}" == "true" ]] ; then
echo "Cannot specify --reset-scm and --dirty-scm options together"
echo
printUsage
exit 1
fi
}
###############################################################################
listTasks() {
echo "Available Tasks:"
echo ""
getAllTasks
for taskFile in ${TASKFILES} ; do
taskName=`bash $taskFile --taskname`
echo " $taskName"
done
echo
}
###############################################################################
downloadPatch () {
PATCHFILE=${TEMPDIR}/test.patch
jiraPage=${TEMPDIR}/jira.txt
curl "${JIRAURLISSUEPREFIX}${JIRAISSUE}" > ${jiraPage}
if [[ `grep -c 'Patch Available' ${jiraPage}` == 0 ]] ; then
echo "$JIRAISSUE is not \"Patch Available\". Exiting."
echo
exit 1
fi
relativePatchURL=`grep -o '"/jira/secure/attachment/[0-9]*/[^"]*' ${jiraPage} \
| grep -v -e 'htm[l]*$' | sort | tail -1 \
| grep -o '/jira/secure/attachment/[0-9]*/[^"]*'`
patchURL="${JIRAHOST}${relativePatchURL}"
patchNum=`echo $patchURL | grep -o '[0-9]*/' | grep -o '[0-9]*'`
curl ${patchURL} > ${PATCHFILE}
if [[ $? != 0 ]] ; then
echo "Could not download patch for ${JIRAISSUE} from ${patchURL}"
echo
cleanupAndExit 1
fi
PATCHNAME=$(echo $patchURL | sed 's/.*\///g')
echo "JIRA ${JIRAISSUE}, patch downloaded at `date` from ${patchURL}"
echo
echo "Patch [$PATCHNAME|$patchURL] downloaded at $(date)" >> ${SUMMARYFILE}
echo "" >> ${SUMMARYFILE}
}
###############################################################################
applyPatch() {
echo "Applying patch" >> $STDOUT
echo "" >> $STDOUT
patch -f -E --dry-run -p0 < ${PATCHFILE} | tee ${REPORTDIR}/APPLY-PATCH.txt \
>> $STDOUT
if [[ ${PIPESTATUS[0]} != 0 ]] ; then
echo "Patch failed to apply to head of branch"
echo "{color:red}-1{color} Patch failed to apply to head of branch" >> ${SUMMARYFILE}
echo "" >> ${SUMMARYFILE}
echo "----------------------------" >> ${SUMMARYFILE}
echo
cleanupAndExit 1
fi
patch -f -E -p0 < ${PATCHFILE} > ${REPORTDIR}/APPLY-PATCH.txt
if [[ $? != 0 ]] ; then
echo "ODD!, dry run passed, but patch failed to apply to head of branch"
echo
cleanupAndExit 1
fi
echo "" >> $STDOUT
echo "Patch applied"
echo "{color:green}+1 PATCH_APPLIES{color}" >> $SUMMARYFILE
echo
}
###############################################################################
run() {
task=`bash $1 --taskname`
if [[ "${TASKSTORUN}" == "" || "${TASKSTORUN}" =~ "${task}" ]] ; then
if [[ ! "${TASKSTOSKIP}" =~ "${task}" ]] ; then
echo " Running test-patch task ${task}"
outputFile="`basename $1`-$2.out"
$1 --op=$2 --tempdir=${TEMPDIR} --reportdir=${REPORTDIR} \
--summaryfile=${SUMMARYFILE} --patchfile=${PATCHFILE} ${MVNPASSTHRU} \
${VERBOSEOPTION} | tee ${TEMPDIR}/${outputFile} >> $STDOUT
if [[ $? != 0 ]] ; then
echo " Failure, check for details ${TEMPDIR}/${outputFile}"
echo
cleanupAndExit 1
fi
fi
fi
}
###############################################################################
getAllTasks() {
TASKFILES=`ls -a bin/test\-patch\-[0-9][0-9]\-*`
}
###############################################################################
prePatchRun() {
echo "Pre patch"
for taskFile in ${TASKFILES} ; do
run $taskFile pre
done
echo
}
###############################################################################
postPatchRun() {
echo "Post patch"
for taskFile in ${TASKFILES} ; do
run $taskFile post
done
echo
}
###############################################################################
createReports() {
echo "Reports"
for taskFile in ${TASKFILES} ; do
run $taskFile report
done
echo
}
###############################################################################
echo
parseArgs "$@"
prepareSCM
prepareTestPatchDirs
echo "" > ${SUMMARYFILE}
if [ "${PATCHFILE}" == "" ] ; then
echo "Testing JIRA ${JIRAISSUE}"
echo
echo "Testing JIRA ${JIRAISSUE}" >> ${SUMMARYFILE}
echo "" >> ${SUMMARYFILE}
else
if [ ! -e ${PATCHFILE} ] ; then
echo "Patch file does not exist"
cleanupAndExit 1
fi
echo "Testing patch ${PATCHFILE}"
echo
echo "Testing patch ${PATCHFILE}" >> ${SUMMARYFILE}
echo "" >> ${SUMMARYFILE}
fi
echo "" >> ${SUMMARYFILE}
if [ "${PATCHFILE}" == "" ] ; then
downloadPatch ${JIRAISSUE}
fi
echo "----------------------------" >> ${SUMMARYFILE}
echo "" >> ${SUMMARYFILE}
getAllTasks
prePatchRun
applyPatch
postPatchRun
createReports
echo "" >> ${SUMMARYFILE}
echo "----------------------------" >> ${SUMMARYFILE}
MINUSONES=`grep -c "\}\-1" ${SUMMARYFILE}`
if [[ $MINUSONES == 0 ]]; then
echo "{color:green}*+1 Overall result, good!, no -1s*{color}" >> ${SUMMARYFILE}
else
echo "{color:red}*-1 Overall result, please check the reported -1(s)*{color}" >> ${SUMMARYFILE}
fi
echo "" >> ${SUMMARYFILE}
WARNINGS=`grep -c "\}WARNING" ${SUMMARYFILE}`
if [[ $WARNINGS != 0 ]]; then
echo "{color:red}. There is at least one warning, please check{color}" >> ${SUMMARYFILE}
fi
echo "" >> ${SUMMARYFILE}
if [ ! -z "${JIRAISSUE}" ]; then
echo "The full output of the test-patch run is available at" >> ${SUMMARYFILE}
echo "" >> ${SUMMARYFILE}
echo ". ${BUILD_URL}" >> ${SUMMARYFILE}
echo "" >> ${SUMMARYFILE}
else
echo
echo "Refer to ${REPORTDIR} for detailed test-patch reports"
echo
fi
cat ${SUMMARYFILE} | sed -e 's/{color}//' -e 's/{color:green}//' -e 's/{color:red}//' -e 's/^\.//' -e 's/^\*//' -e 's/\*$//' > ${SUMMARYFILETXT}
cat ${SUMMARYFILETXT}
cleanupAndExit `expr $MINUSONES != 0`
bookkeeper-release-4.2.4/bin/test-patch-00-clean 0000775 0000000 0000000 00000004706 12445073612 0021347 0 ustar 00root root 0000000 0000000 #!/bin/bash
#
# Licensed 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.
if [ "${TESTPATCHDEBUG}" == "true" ] ; then
set -x
fi
BASEDIR=$(pwd)
TASKNAME="CLEAN"
OP=""
TEMPDIR=""
REPORTDIR=""
SUMMARYFILE=""
MVNPASSTHRU=""
###############################################################################
cleanupAndExit() {
exit $1
}
###############################################################################
printUsage() {
echo "Usage: $0 --taskname | (--op=pre|post|report --tempdir=) [-D...] [-P...]"
echo
}
###############################################################################
parseArgs() {
for i in $*
do
case $i in
--taskname)
echo ${TASKNAME}
exit 0
;;
--op=*)
OP=${i#*=}
;;
--tempdir=*)
TEMPDIR=${i#*=}
;;
--reportdir=*)
REPORTDIR=${i#*=}
;;
--summaryfile=*)
SUMMARYFILE=${i#*=}
;;
-D*)
MVNPASSTHRU="${MVNPASSTHRU} $i"
;;
-P*)
MVNPASSTHRU="${MVNPASSTHRU} $i"
;;
esac
done
if [[ "${OP}" == "" || "${TEMPDIR}" == "" ]] ; then
echo "Missing options"
echo
printUsage
cleanupAndExit 1
fi
if [[ "${OP}" != "pre" && "${OP}" != "post" && "${OP}" != "report" ]] ; then
echo "Invalid operation"
echo
printUsage
cleanupAndExit 1
fi
}
###############################################################################
parseArgs "$@"
case $OP in
pre)
mvn clean ${MVNPASSTHRU} > ${TEMPDIR}/${TASKNAME}.txt
EXITCODE=$?
# removing files created by dependency:copy-dependencies
rm -f */lib/*
exit $EXITCODE
;;
post)
mvn clean ${MVNPASSTHRU} >> ${TEMPDIR}/${TASKNAME}.txt
EXITCODE=$?
;;
report)
echo "{color:green}+1 ${TASKNAME}{color}" >> $SUMMARYFILE
;;
esac
exit 0
bookkeeper-release-4.2.4/bin/test-patch-05-patch-raw-analysis 0000775 0000000 0000000 00000012362 12445073612 0023776 0 ustar 00root root 0000000 0000000 #!/bin/bash
#
# Licensed 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.
source $(dirname "$0")/raw-check-patch
if [ "${TESTPATCHDEBUG}" == "true" ] ; then
set -x
fi
BASEDIR=$(pwd)
TASKNAME="RAW_PATCH_ANALYSIS"
OP=""
TEMPDIR=""
REPORTDIR=""
SUMMARYFILE=""
PATCHFILE=""
###############################################################################
cleanupAndExit() {
exit $1
}
###############################################################################
printUsage() {
echo "Usage: $0 --taskname | (--op=pre|post|report --tempdir= --reportdir= --summaryfile=)"
echo
}
###############################################################################
parseArgs() {
for i in $*
do
case $i in
--taskname)
echo ${TASKNAME}
exit 0
;;
--op=*)
OP=${i#*=}
;;
--tempdir=*)
TEMPDIR=${i#*=}
;;
--reportdir=*)
REPORTDIR=${i#*=}
;;
--summaryfile=*)
SUMMARYFILE=${i#*=}
;;
--patchfile=*)
PATCHFILE=${i#*=}
;;
esac
done
if [[ "${TASKNAME}" == "" || "${OP}" == "" || "${TEMPDIR}" == "" || "${REPORTDIR}" == "" || "${SUMMARYFILE}" == "" || "${PATCHFILE}" == "" ]] ; then
echo "Missing options"
echo
printUsage
cleanupAndExit 1
fi
if [[ "${OP}" != "pre" && "${OP}" != "post" && "${OP}" != "report" ]] ; then
echo "Invalid operation"
echo
printUsage
cleanupAndExit 1
fi
}
###############################################################################
checkNoAuthors() {
TMPFILE=$TEMPDIR/$TASKNAME-authors.txt
printAuthors $PATCHFILE > $TMPFILE
authorTags=$(wc -l $TMPFILE | awk '{print $1}')
if [[ ${authorTags} != 0 ]] ; then
REPORT+=("{color:red}-1{color} the patch seems to contain ${authorTags} line(s) with @author tags")
REPORT+=("$(cat $TMPFILE)")
else
REPORT+=("{color:green}+1{color} the patch does not introduce any @author tags")
fi
}
###############################################################################
checkNoTabs() {
TMPFILE=$TEMPDIR/$TASKNAME-tabs.txt
printTabs $PATCHFILE > $TMPFILE
tabs=$(wc -l $TMPFILE | awk '{print $1}')
if [[ ${tabs} != 0 ]] ; then
REPORT+=("{color:red}-1{color} the patch contains ${tabs} line(s) with tabs")
REPORT+=("$(cat $TMPFILE)")
else
REPORT+=("{color:green}+1{color} the patch does not introduce any tabs")
fi
}
###############################################################################
checkNoTrailingSpaces() {
TMPFILE=$TEMPDIR/$TASKNAME-trailingspaces.txt
printTrailingSpaces $PATCHFILE > $TMPFILE
trailingSpaces=$(wc -l $TMPFILE | awk '{print $1}')
if [[ ${trailingSpaces} != 0 ]] ; then
REPORT+=("{color:red}-1{color} the patch contains ${trailingSpaces} line(s) with trailing spaces")
REPORT+=("$(cat $TMPFILE)")
else
REPORT+=("{color:green}+1{color} the patch does not introduce any trailing spaces")
fi
}
###############################################################################
checkLinesLength() {
TMPFILE=$TEMPDIR/$TASKNAME-trailingspaces.txt
printLongLines $PATCHFILE > $TMPFILE
longLines=$(wc -l $TMPFILE | awk '{print $1}')
if [[ ${longLines} != 0 ]] ; then
REPORT+=("{color:red}-1{color} the patch contains ${longLines} line(s) longer than 120 characters")
REPORT+=("$(cat $TMPFILE)")
else
REPORT+=("{color:green}+1{color} the patch does not introduce any line longer than 120")
fi
}
###############################################################################
checkForTestcases() {
testcases=`grep -c -i -e '^+++.*/test' ${PATCHFILE}`
if [[ ${testcases} == 0 ]] ; then
REPORT+=("{color:red}-1{color} the patch does not add/modify any testcase")
#reverting for summary +1 calculation
testcases=1
else
REPORT+=("{color:green}+1{color} the patch does adds/modifies ${testcases} testcase(s)")
#reverting for summary +1 calculation
testcases=0
fi
}
###############################################################################
parseArgs "$@"
case $OP in
pre)
;;
post)
;;
report)
REPORT=()
checkNoAuthors
checkNoTabs
checkNoTrailingSpaces
checkLinesLength
checkForTestcases
total=`expr $authorTags + $tabs + $trailingSpaces + $longLines + $testcases`
if [[ $total == 0 ]] ; then
echo "{color:green}+1 ${TASKNAME}{color}" >> $SUMMARYFILE
else
echo "{color:red}-1 ${TASKNAME}{color}" >> $SUMMARYFILE
fi
for line in "${REPORT[@]}" ; do
echo ". ${line}" >> $SUMMARYFILE
done
;;
esac
exit 0
bookkeeper-release-4.2.4/bin/test-patch-08-rat 0000775 0000000 0000000 00000007401 12445073612 0021056 0 ustar 00root root 0000000 0000000 #!/bin/bash
#
# Licensed 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.
if [ "${TESTPATCHDEBUG}" == "true" ] ; then
set -x
fi
BASEDIR=$(pwd)
TASKNAME="RAT"
OP=""
TEMPDIR=""
REPORTDIR=""
SUMMARYFILE=""
STDOUT="/dev/null"
MVNPASSTHRU=""
###############################################################################
cleanupAndExit() {
exit $1
}
###############################################################################
printUsage() {
echo "Usage: $0 --taskname | (--op=pre|post|report --tempdir= --reportdir= --summaryfile=) [-D...] [-P...]"
echo
}
###############################################################################
parseArgs() {
for i in $*
do
case $i in
--taskname)
echo ${TASKNAME}
exit 0
;;
--op=*)
OP=${i#*=}
;;
--tempdir=*)
TEMPDIR=${i#*=}
;;
--reportdir=*)
REPORTDIR=${i#*=}
;;
--summaryfile=*)
SUMMARYFILE=${i#*=}
;;
--verbose)
STDOUT="/dev/stdout"
;;
-D*)
MVNPASSTHRU="${MVNPASSTHRU} $i"
;;
-P*)
MVNPASSTHRU="${MVNPASSTHRU} $i"
;;
esac
done
if [[ "${TASKNAME}" == "" || "${OP}" == "" || "${TEMPDIR}" == "" || "${REPORTDIR}" == "" || "${SUMMARYFILE}" == "" ]] ; then
echo "Missing options"
echo
printUsage
cleanupAndExit 1
fi
if [[ "${OP}" != "pre" && "${OP}" != "post" && "${OP}" != "report" ]] ; then
echo "Invalid operation"
echo
printUsage
cleanupAndExit 1
fi
}
###############################################################################
checkForWarnings() {
cleanWarns=`grep -c '\!?????' ${REPORTDIR}/${TASKNAME}-clean.txt`
patchWarns=`grep -c '\!?????' ${REPORTDIR}/${TASKNAME}-patch.txt`
newWarns=`expr $patchWarns - $cleanWarns`
if [[ $newWarns -le 0 ]] ; then
REPORT+=("{color:green}+1{color} the patch does not seem to introduce new RAT warnings")
newWarns=0
else
REPORT+=("{color:red}-1{color} the patch seems to introduce $newWarns new RAT warning(s)")
newWarns=1
fi
if [[ $cleanWarns != 0 ]] ; then
REPORT+=("{color:red}WARNING: the current HEAD has $cleanWarns RAT warning(s), they should be addressed ASAP{color}")
fi
}
###############################################################################
copyRatFiles() {
TAG=$1
rm -f ${REPORTDIR}/${TASKNAME}-$TAG.txt
for f in $(find . -name rat.txt); do
cat $f >> ${REPORTDIR}/${TASKNAME}-$TAG.txt
done
}
###############################################################################
parseArgs "$@"
case $OP in
pre)
mvn apache-rat:check ${MVNPASSTHRU} > $STDOUT
copyRatFiles clean
;;
post)
mvn apache-rat:check ${MVNPASSTHRU} > $STDOUT
copyRatFiles patch
;;
report)
checkForWarnings
if [[ $newWarns == 0 ]] ; then
echo "{color:green}+1 ${TASKNAME}{color}" >> $SUMMARYFILE
else
echo "{color:red}-1 ${TASKNAME}{color}" >> $SUMMARYFILE
fi
for line in "${REPORT[@]}" ; do
echo ". ${line}" >> $SUMMARYFILE
done
;;
esac
exit 0
bookkeeper-release-4.2.4/bin/test-patch-09-javadoc 0000775 0000000 0000000 00000007202 12445073612 0021677 0 ustar 00root root 0000000 0000000 #!/bin/bash
#
# Licensed 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.
if [ "${TESTPATCHDEBUG}" == "true" ] ; then
set -x
fi
BASEDIR=$(pwd)
TASKNAME="JAVADOC"
OP=""
TEMPDIR=""
REPORTDIR=""
SUMMARYFILE=""
MVNPASSTHRU=""
###############################################################################
cleanupAndExit() {
exit $1
}
###############################################################################
printUsage() {
echo "Usage: $0 --taskname | (--op=pre|post|report --tempdir= --reportdir= --summaryfile=) [-D...] [-P...]"
echo
}
###############################################################################
parseArgs() {
for i in $*
do
case $i in
--taskname)
echo ${TASKNAME}
exit 0
;;
--op=*)
OP=${i#*=}
;;
--tempdir=*)
TEMPDIR=${i#*=}
;;
--reportdir=*)
REPORTDIR=${i#*=}
;;
--summaryfile=*)
SUMMARYFILE=${i#*=}
;;
-D*)
MVNPASSTHRU="${MVNPASSTHRU} $i"
;;
-P*)
MVNPASSTHRU="${MVNPASSTHRU} $i"
;;
esac
done
if [[ "${TASKNAME}" == "" || "${OP}" == "" || "${TEMPDIR}" == "" || "${REPORTDIR}" == "" || "${SUMMARYFILE}" == "" ]] ; then
echo "Missing options"
echo
printUsage
cleanupAndExit 1
fi
if [[ "${OP}" != "pre" && "${OP}" != "post" && "${OP}" != "report" ]] ; then
echo "Invalid operation"
echo
printUsage
cleanupAndExit 1
fi
}
###############################################################################
checkForWarnings() {
cleanWarns=`grep '\[WARNING\]' ${REPORTDIR}/${TASKNAME}-clean.txt | awk '/Javadoc Warnings/,EOF' | grep warning | awk 'BEGIN {total = 0} {total += 1} END {print total}'`
patchWarns=`grep '\[WARNING\]' ${REPORTDIR}/${TASKNAME}-patch.txt | awk '/Javadoc Warnings/,EOF' | grep warning | awk 'BEGIN {total = 0} {total += 1} END {print total}'`
newWarns=`expr $patchWarns - $cleanWarns`
if [[ $newWarns -le 0 ]] ; then
REPORT+=("{color:green}+1{color} the patch does not seem to introduce new Javadoc warnings")
newWarns=0
else
REPORT+=("{color:red}-1{color} the patch seems to introduce $newWarns new Javadoc warning(s)")
newWarns=1
fi
if [[ $cleanWarns != 0 ]] ; then
REPORT+=("{color:red}WARNING{color}: the current HEAD has $cleanWarns Javadoc warning(s)")
fi
}
###############################################################################
parseArgs "$@"
case $OP in
pre)
mvn clean javadoc:aggregate ${MVNPASSTHRU} > ${REPORTDIR}/${TASKNAME}-clean.txt
;;
post)
mvn clean javadoc:aggregate ${MVNPASSTHRU} > ${REPORTDIR}/${TASKNAME}-patch.txt
;;
report)
checkForWarnings
if [[ $newWarns == 0 ]] ; then
echo "{color:green}+1 ${TASKNAME}{color}" >> $SUMMARYFILE
else
echo "{color:red}-1 ${TASKNAME}{color}" >> $SUMMARYFILE
fi
for line in "${REPORT[@]}" ; do
echo ". ${line}" >> $SUMMARYFILE
done
;;
esac
exit 0
bookkeeper-release-4.2.4/bin/test-patch-10-compile 0000775 0000000 0000000 00000011115 12445073612 0021706 0 ustar 00root root 0000000 0000000 #!/bin/bash
#
# Licensed 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.
if [ "${TESTPATCHDEBUG}" == "true" ] ; then
set -x
fi
BASEDIR=$(pwd)
TASKNAME="COMPILE"
OP=""
TEMPDIR=""
REPORTDIR=""
SUMMARYFILE=""
STDOUT="/dev/null"
MVNPASSTHRU=""
###############################################################################
cleanupAndExit() {
exit $1
}
###############################################################################
printUsage() {
echo "Usage: $0 --taskname | (--op=pre|post|report --tempdir= --reportdir= --summaryfile=) [--verbose] [-D...] [-P...]"
echo
}
###############################################################################
parseArgs() {
for i in $*
do
case $i in
--taskname)
echo ${TASKNAME}
exit 0
;;
--op=*)
OP=${i#*=}
;;
--tempdir=*)
TEMPDIR=${i#*=}
;;
--reportdir=*)
REPORTDIR=${i#*=}
;;
--summaryfile=*)
SUMMARYFILE=${i#*=}
;;
--verbose)
STDOUT="/dev/stdout"
;;
-D*)
MVNPASSTHRU="${MVNPASSTHRU} $i"
;;
-P*)
MVNPASSTHRU="${MVNPASSTHRU} $i"
;;
esac
done
if [[ "${TASKNAME}" == "" || "${OP}" == "" || "${TEMPDIR}" == "" || "${REPORTDIR}" == "" || "${SUMMARYFILE}" == "" ]] ; then
echo "Missing options"
echo
printUsage
cleanupAndExit 1
fi
if [[ "${OP}" != "pre" && "${OP}" != "post" && "${OP}" != "report" ]] ; then
echo "Invalid operation"
echo
printUsage
cleanupAndExit 1
fi
}
###############################################################################
checkForWarnings() {
grep '\[WARNING\]' ${REPORTDIR}/${TASKNAME}-clean.txt > ${TEMPDIR}/${TASKNAME}-javacwarns-clean.txt
grep '\[WARNING\]' ${REPORTDIR}/${TASKNAME}-patch.txt > ${TEMPDIR}/${TASKNAME}-javacwarns-patch.txt
cleanWarns=`cat ${TEMPDIR}/${TASKNAME}-javacwarns-clean.txt | awk 'BEGIN {total = 0} {total += 1} END {print total}'`
patchWarns=`cat ${TEMPDIR}/${TASKNAME}-javacwarns-patch.txt | awk 'BEGIN {total = 0} {total += 1} END {print total}'`
newWarns=`expr $patchWarns - $cleanWarns`
if [[ $newWarns -le 0 ]] ; then
REPORT+=("{color:green}+1{color} the patch does not seem to introduce new javac warnings")
newWarns=0
else
REPORT+=("{color:red}-1{color} the patch seems to introduce $newWarns new javac warning(s)")
newWarns=1
fi
if [[ $cleanWarns != 0 ]] ; then
REPORT+=("{color:red}WARNING{color}: the current HEAD has $cleanWarns javac warning(s)")
fi
}
###############################################################################
parseArgs "$@"
case $OP in
pre)
mvn clean package -DskipTests ${MVNPASSTHRU} | tee ${REPORTDIR}/${TASKNAME}-clean.txt >> $STDOUT
if [[ ${PIPESTATUS[0]} == 0 ]] ; then
echo "{color:green}+1{color} HEAD compiles" >> ${TEMPDIR}/${TASKNAME}-compile.txt
else
echo "{color:red}-1{color} HEAD does not compile" >> ${TEMPDIR}/${TASKNAME}-compile.txt
fi
;;
post)
mvn clean package -DskipTests ${MVNPASSTHRU} | tee ${REPORTDIR}/${TASKNAME}-patch.txt >> $STDOUT
if [[ ${PIPESTATUS[0]} == 0 ]] ; then
echo "{color:green}+1{color} patch compiles" >> ${TEMPDIR}/${TASKNAME}-compile.txt
else
echo "{color:red}-1{color} patch does not compile" >> ${TEMPDIR}/${TASKNAME}-compile.txt
fi
;;
report)
REPORT=()
compileErrors=0
while read line; do
REPORT+=("$line")
if [[ "$line" =~ "-1" ]] ; then
compileErrors=1
fi
done < ${TEMPDIR}/${TASKNAME}-compile.txt
checkForWarnings
total=`expr $compileErrors + $newWarns`
if [[ $total == 0 ]] ; then
echo "{color:green}+1 ${TASKNAME}{color}" >> $SUMMARYFILE
else
echo "{color:red}-1 ${TASKNAME}{color}" >> $SUMMARYFILE
fi
for line in "${REPORT[@]}" ; do
echo ". ${line}" >> $SUMMARYFILE
done
;;
esac
exit 0
bookkeeper-release-4.2.4/bin/test-patch-11-findbugs 0000775 0000000 0000000 00000011014 12445073612 0022056 0 ustar 00root root 0000000 0000000 #!/bin/bash
#
# Licensed 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.
if [ "${TESTPATCHDEBUG}" == "true" ] ; then
set -x
fi
BASEDIR=$(pwd)
TASKNAME="FINDBUGS"
OP=""
TEMPDIR=""
REPORTDIR=""
SUMMARYFILE=""
STDOUT="/dev/null"
MVNPASSTHRU=""
###############################################################################
cleanupAndExit() {
exit $1
}
###############################################################################
printUsage() {
echo "Usage: $0 --taskname | (--op=pre|post|report --tempdir= --reportdir= --summaryfile=) [-D...] [-P...]"
echo
}
###############################################################################
parseArgs() {
for i in $*
do
case $i in
--taskname)
echo ${TASKNAME}
exit 0
;;
--op=*)
OP=${i#*=}
;;
--tempdir=*)
TEMPDIR=${i#*=}
;;
--reportdir=*)
REPORTDIR=${i#*=}
;;
--summaryfile=*)
SUMMARYFILE=${i#*=}
;;
--verbose)
STDOUT="/dev/stdout"
;;
-D*)
MVNPASSTHRU="${MVNPASSTHRU} $i"
;;
-P*)
MVNPASSTHRU="${MVNPASSTHRU} $i"
;;
esac
done
if [[ "${TASKNAME}" == "" || "${OP}" == "" || "${TEMPDIR}" == "" || "${REPORTDIR}" == "" || "${SUMMARYFILE}" == "" ]] ; then
echo "Missing options"
echo
printUsage
cleanupAndExit 1
fi
if [[ "${OP}" != "pre" && "${OP}" != "post" && "${OP}" != "report" ]] ; then
echo "Invalid operation"
echo
printUsage
cleanupAndExit 1
fi
}
###############################################################################
checkForWarnings() {
cleanBugs=0
patchBugs=0
for m in $(getModules); do
MODNAME=$(echo $m | sed 's/\///')
m_cleanBugs=$(cat ${REPORTDIR}/${TASKNAME}-${MODNAME}-clean.xml \
| sed 's/<\/BugInstance>/<\/BugInstance>\n/g' | grep BugInstance | wc -l)
m_patchBugs=$(cat ${REPORTDIR}/${TASKNAME}-${MODNAME}-patch.xml \
| sed 's/<\/BugInstance>/<\/BugInstance>\n/g' | grep BugInstance | wc -l)
m_newBugs=`expr $m_patchBugs - $m_cleanBugs`
if [[ $m_newBugs != 0 ]] ; then
BUGMODULES="$MODNAME $BUGMODULES"
fi
cleanBugs=$(($cleanBugs+$m_cleanBugs))
patchBugs=$(($patchBugs+$m_patchBugs))
done
BUGMODULES=$(echo $BUGMODULES | sed 's/^ *//' | sed 's/ *$//')
newBugs=`expr $patchBugs - $cleanBugs`
if [[ $newBugs -le 0 ]] ; then
REPORT+=("{color:green}+1{color} the patch does not seem to introduce new Findbugs warnings")
newBugs=0
else
REPORT+=("{color:red}-1{color} the patch seems to introduce $patchBugs new Findbugs warning(s) in module(s) [$BUGMODULES]")
newBugs=1
fi
if [[ $cleanBugs != 0 ]] ; then
REPORT+=("{color:red}WARNING: the current HEAD has $cleanWarns Findbugs warning(s), they should be addressed ASAP{color}")
fi
}
###############################################################################
getModules() {
find . -name pom.xml | sed 's/^.\///' | sed 's/pom.xml$//' | grep -v compat
}
###############################################################################
copyFindbugsXml() {
TAG=$1
for m in $(getModules); do
MODNAME=$(echo $m | sed 's/\///')
cp ${m}target/findbugsXml.xml ${REPORTDIR}/${TASKNAME}-${MODNAME}-$TAG.xml
done
}
parseArgs "$@"
case $OP in
pre)
mvn findbugs:findbugs ${MVNPASSTHRU} > $STDOUT
copyFindbugsXml clean
;;
post)
mvn findbugs:findbugs ${MVNPASSTHRU} > $STDOUT
copyFindbugsXml patch
;;
report)
checkForWarnings
if [[ $newBugs == 0 ]] ; then
echo "{color:green}+1 ${TASKNAME}{color}" >> $SUMMARYFILE
else
echo "{color:red}-1 ${TASKNAME}{color}" >> $SUMMARYFILE
fi
for line in "${REPORT[@]}" ; do
echo ". ${line}" >> $SUMMARYFILE
done
;;
esac
exit 0
bookkeeper-release-4.2.4/bin/test-patch-20-tests 0000775 0000000 0000000 00000010537 12445073612 0021430 0 ustar 00root root 0000000 0000000 #!/bin/bash
#
# Licensed 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.
if [ "${TESTPATCHDEBUG}" == "true" ] ; then
set -x
fi
BASEDIR=$(pwd)
TASKNAME="TESTS"
OP=""
TEMPDIR=""
REPORTDIR=""
SUMMARYFILE=""
STDOUT="/dev/null"
MVNPASSTHRU=""
###############################################################################
cleanupAndExit() {
exit $1
}
###############################################################################
printUsage() {
echo "Usage: $0 --taskname | (--op=pre|post|report --tempdir= --reportdir= --summaryfile=) [--verbose] [-D...] [-P...]"
echo
}
###############################################################################
parseArgs() {
for i in $*
do
case $i in
--taskname)
echo ${TASKNAME}
exit 0
;;
--op=*)
OP=${i#*=}
;;
--tempdir=*)
TEMPDIR=${i#*=}
;;
--reportdir=*)
REPORTDIR=${i#*=}
;;
--summaryfile=*)
SUMMARYFILE=${i#*=}
;;
--verbose)
STDOUT="/dev/stdout"
;;
-D*)
MVNPASSTHRU="${MVNPASSTHRU} $i"
;;
-P*)
MVNPASSTHRU="${MVNPASSTHRU} $i"
;;
esac
done
if [[ "${TASKNAME}" == "" || "${OP}" == "" || "${TEMPDIR}" == "" || "${REPORTDIR}" == "" || "${SUMMARYFILE}" == "" ]] ; then
echo "Missing options"
echo
printUsage
cleanupAndExit 1
fi
if [[ "${OP}" != "pre" && "${OP}" != "post" && "${OP}" != "report" ]] ; then
echo "Invalid operation"
echo
printUsage
cleanupAndExit 1
fi
}
###############################################################################
parseArgs "$@"
case $OP in
pre)
;;
post)
# must use package instead of test so that compat-deps shaded jars are correct
mvn package ${MVNPASSTHRU} -Dmaven.test.failure.ignore=true \
-Dmaven.test.error.ignore=true -fae \
-Dtest.timeout=7200 | tee ${TEMPDIR}/${TASKNAME}.out >> $STDOUT
exitCode=${PIPESTATUS[0]}
echo "$exitCode" > ${TEMPDIR}/${TASKNAME}.exitCode
;;
report)
failedTests=` find . -name '*\.txt' | grep target/surefire-reports | xargs grep "<<< FAILURE" | grep -v "Tests run:" | sed 's/.*\.txt\://' | sed 's/ .*//'`
testsRun=`grep "Tests run:" ${TEMPDIR}/${TASKNAME}.out | grep -v " Time elapsed:" | awk '{print $3}' | sed 's/,//' | awk 'BEGIN {count=0} {count=count+$1} END {print count}'`
testsFailed=`grep "Tests run:" ${TEMPDIR}/${TASKNAME}.out | grep -v " Time elapsed:" | awk '{print $5}' | sed 's/,//' | awk 'BEGIN {count=0} {count=count+$1} END {print count}'`
testsErrors=`grep "Tests run:" ${TEMPDIR}/${TASKNAME}.out | grep -v " Time elapsed:" | awk '{print $7}' | sed 's/,//' | awk 'BEGIN {count=0} {count=count+$1} END {print count}'`
hasFailures=`expr $testsFailed + $testsErrors`
testsExitCode=`cat ${TEMPDIR}/${TASKNAME}.exitCode`
if [[ $hasFailures != 0 ]] ; then
echo "{color:red}-1 ${TASKNAME}{color}" >> $SUMMARYFILE
echo ". Tests run: $testsRun" >> $SUMMARYFILE
echo ". Tests failed: $testsFailed" >> $SUMMARYFILE
echo ". Tests errors: $testsErrors" >> $SUMMARYFILE
echo "" >> ${SUMMARYFILE}
echo ". The patch failed the following testcases:" >> $SUMMARYFILE
echo "" >> ${SUMMARYFILE}
echo "${failedTests}" | sed 's/^/. /' >> $SUMMARYFILE
echo "" >> ${SUMMARYFILE}
else
if [[ "$testsExitCode" != "0" ]] ; then
echo "{color:red}-1 ${TASKNAME}{color} - patch does not compile, cannot run testcases" >> $SUMMARYFILE
else
echo "{color:green}+1 ${TASKNAME}{color}" >> $SUMMARYFILE
echo ". Tests run: $testsRun" >> $SUMMARYFILE
fi
fi
;;
esac
exit 0
bookkeeper-release-4.2.4/bin/test-patch-30-dist 0000775 0000000 0000000 00000005714 12445073612 0021233 0 ustar 00root root 0000000 0000000 #!/bin/bash
#
# Licensed 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.
if [ "${TESTPATCHDEBUG}" == "true" ] ; then
set -x
fi
BASEDIR=$(pwd)
TASKNAME="DISTRO"
OP=""
TEMPDIR=""
REPORTDIR=""
SUMMARYFILE=""
STDOUT="/dev/null"
MVNPASSTHRU=""
###############################################################################
cleanupAndExit() {
exit $1
}
###############################################################################
printUsage() {
echo "Usage: $0 --taskname | (--op=pre|post|report --tempdir= --reportdir= --summaryfile=) [--verbose] [-D...] [-P...]"
echo
}
###############################################################################
parseArgs() {
for i in $*
do
case $i in
--taskname)
echo ${TASKNAME}
exit 0
;;
--op=*)
OP=${i#*=}
;;
--tempdir=*)
TEMPDIR=${i#*=}
;;
--reportdir=*)
REPORTDIR=${i#*=}
;;
--summaryfile=*)
SUMMARYFILE=${i#*=}
;;
--verbose)
STDOUT="/dev/stdout"
;;
-D*)
MVNPASSTHRU="${MVNPASSTHRU} $i"
;;
-P*)
MVNPASSTHRU="${MVNPASSTHRU} $i"
;;
esac
done
if [[ "${TASKNAME}" == "" || "${OP}" == "" || "${TEMPDIR}" == "" || "${REPORTDIR}" == "" || "${SUMMARYFILE}" == "" ]] ; then
echo "Missing options"
echo
printUsage
cleanupAndExit 1
fi
if [[ "${OP}" != "pre" && "${OP}" != "post" && "${OP}" != "report" ]] ; then
echo "Invalid operation"
echo
printUsage
cleanupAndExit 1
fi
}
###############################################################################
parseArgs "$@"
case $OP in
pre)
;;
post)
mvn package assembly:single -DskipTests | tee ${REPORTDIR}/${TASKNAME}.out >> $STDOUT
exitCode=${PIPESTATUS[0]}
echo "$exitCode" > ${TEMPDIR}/${TASKNAME}.exitCode
;;
report)
exitCode=`cat ${TEMPDIR}/${TASKNAME}.exitCode`
if [[ "$exitCode" != "0" ]] ; then
echo "{color:red}-1 ${TASKNAME}{color}" >> $SUMMARYFILE
echo ". {color:red}-1{color} distro tarball fails with the patch" >> $SUMMARYFILE
else
echo "{color:green}+1 ${TASKNAME}{color}" >> $SUMMARYFILE
echo ". {color:green}+1{color} distro tarball builds with the patch " >> $SUMMARYFILE
fi
;;
esac
exit 0
bookkeeper-release-4.2.4/bookkeeper-benchmark/ 0000775 0000000 0000000 00000000000 12445073612 0021347 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/bin/ 0000775 0000000 0000000 00000000000 12445073612 0022117 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/bin/benchmark 0000775 0000000 0000000 00000010337 12445073612 0024003 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
#
#/**
# * Copyright 2007 The Apache Software Foundation
# *
# * 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.
# */
# check if net.ipv6.bindv6only is set to 1
bindv6only=$(/sbin/sysctl -n net.ipv6.bindv6only 2> /dev/null)
if [ -n "$bindv6only" ] && [ "$bindv6only" -eq "1" ]
then
echo "Error: \"net.ipv6.bindv6only\" is set to 1 - Java networking could be broken"
echo "For more info (the following page also applies to bookkeeper): http://wiki.apache.org/hadoop/HadoopIPv6"
exit 1
fi
BINDIR=`dirname "$0"`
BENCH_HOME=`cd $BINDIR/..;pwd`
RELEASE_JAR=`ls $BENCH_HOME/bookkeeper-benchmark-*.jar 2> /dev/null | tail -1`
if [ $? == 0 ]; then
BENCHMARK_JAR=$RELEASE_JAR
fi
BUILT_JAR=`ls $BENCH_HOME/target/bookkeeper-benchmark-*.jar 2> /dev/null | tail -1`
if [ $? != 0 ] && [ ! -e "$BENCHMARK_JAR" ]; then
echo "\nCouldn't find benchmark jar.";
echo "Make sure you've run 'mvn package'\n";
exit 1;
elif [ -e "$BUILT_JAR" ]; then
BENCHMARK_JAR=$BUILT_JAR
fi
benchmark_help() {
cat <
where command is one of:
writes Benchmark throughput and latency for writes
reads Benchmark throughput and latency for reads
bookie Benchmark an individual bookie
help This help message
use -help with individual commands for more options. For example,
$0 writes -help
or command is the full name of a class with a defined main() method.
Environment variables:
BENCHMARK_LOG_CONF Log4j configuration file (default: conf/log4j.properties)
BENCHMARK_EXTRA_OPTS Extra options to be passed to the jvm
BENCHMARK_EXTRA_CLASSPATH Add extra paths to the bookkeeper classpath
EOF
}
add_maven_deps_to_classpath() {
MVN="mvn"
if [ "$MAVEN_HOME" != "" ]; then
MVN=${MAVEN_HOME}/bin/mvn
fi
# Need to generate classpath from maven pom. This is costly so generate it
# and cache it. Save the file into our target dir so a mvn clean will get
# clean it up and force us create a new one.
f="${BENCH_HOME}/target/cached_classpath.txt"
if [ ! -f "${f}" ]
then
${MVN} -f "${BENCH_HOME}/pom.xml" dependency:build-classpath -Dmdep.outputFile="${f}" &> /dev/null
fi
BENCHMARK_CLASSPATH=${CLASSPATH}:`cat "${f}"`
}
if [ -d "$BENCH_HOME/lib" ]; then
for i in $BENCH_HOME/lib/*.jar; do
BENCHMARK_CLASSPATH=$BENCHMARK_CLASSPATH:$i
done
else
add_maven_deps_to_classpath
fi
# if no args specified, show usage
if [ $# = 0 ]; then
benchmark_help;
exit 1;
fi
# get arguments
COMMAND=$1
shift
BENCHMARK_CLASSPATH="$BENCHMARK_JAR:$BENCHMARK_CLASSPATH:$BENCHMARK_EXTRA_CLASSPATH"
BENCHMARK_LOG_CONF=${BENCHMARK_LOG_CONF:-$BENCH_HOME/conf/log4j.properties}
if [ "$BENCHMARK_LOG_CONF" != "" ]; then
BENCHMARK_CLASSPATH="`dirname $BENCHMARK_LOG_CONF`:$BENCHMARK_CLASSPATH"
OPTS="$OPTS -Dlog4j.configuration=`basename $BENCHMARK_LOG_CONF`"
fi
OPTS="-cp $BENCHMARK_CLASSPATH $OPTS $BENCHMARK_EXTRA_OPTS"
OPTS="$OPTS $BENCHMARK_EXTRA_OPTS"
# Disable ipv6 as it can cause issues
OPTS="$OPTS -Djava.net.preferIPv4Stack=true"
if [ $COMMAND == "writes" ]; then
exec java $OPTS org.apache.bookkeeper.benchmark.BenchThroughputLatency $@
elif [ $COMMAND == "reads" ]; then
exec java $OPTS org.apache.bookkeeper.benchmark.BenchReadThroughputLatency $@
elif [ $COMMAND == "bookie" ]; then
exec java $OPTS org.apache.bookkeeper.benchmark.BenchBookie $@
elif [ $COMMAND == "help" ]; then
benchmark_help;
else
exec java $OPTS $COMMAND $@
fi
bookkeeper-release-4.2.4/bookkeeper-benchmark/conf/ 0000775 0000000 0000000 00000000000 12445073612 0022274 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/conf/log4j.properties 0000664 0000000 0000000 00000005246 12445073612 0025440 0 ustar 00root root 0000000 0000000 #
#
# 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.
#
#
#
# Bookkeeper Logging Configuration
#
# Format is " (, )+
# DEFAULT: console appender only
log4j.rootLogger=ERROR, CONSOLE
# Example with rolling log file
#log4j.rootLogger=DEBUG, CONSOLE, ROLLINGFILE
# Example with rolling log file and tracing
#log4j.rootLogger=TRACE, CONSOLE, ROLLINGFILE, TRACEFILE
#
# Log INFO level and above messages to the console
#
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=INFO
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %-5p - [%t:%C{1}@%L] - %m%n
log4j.logger.org.apache.bookkeeper.benchmark=INFO
#
# Add ROLLINGFILE to rootLogger to get log file output
# Log DEBUG level and above messages to a log file
log4j.appender.ROLLINGFILE=org.apache.log4j.DailyRollingFileAppender
log4j.appender.ROLLINGFILE.Threshold=DEBUG
log4j.appender.ROLLINGFILE.File=bookkeeper-benchmark.log
log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p - [%t:%C{1}@%L] - %m%n
# Max log file size of 10MB
log4j.appender.ROLLINGFILE.MaxFileSize=10MB
# uncomment the next line to limit number of backup files
#log4j.appender.ROLLINGFILE.MaxBackupIndex=10
log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n
#
# Add TRACEFILE to rootLogger to get log file output
# Log DEBUG level and above messages to a log file
log4j.appender.TRACEFILE=org.apache.log4j.FileAppender
log4j.appender.TRACEFILE.Threshold=TRACE
log4j.appender.TRACEFILE.File=bookkeeper_trace.log
log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout
### Notice we are including log4j's NDC here (%x)
log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L][%x] - %m%n
bookkeeper-release-4.2.4/bookkeeper-benchmark/pom.xml 0000664 0000000 0000000 00000012071 12445073612 0022665 0 ustar 00root root 0000000 0000000
4.0.0
bookkeeper
org.apache.bookkeeper
4.2.4
org.apache.bookkeeper
bookkeeper-benchmark
bookkeeper-benchmark
http://maven.apache.org
UTF-8
maven-assembly-plugin
2.2.1
true
org.apache.maven.plugins
maven-surefire-plugin
target/latencyDump.dat
junit
junit
4.8.1
test
org.slf4j
slf4j-api
1.6.4
org.slf4j
slf4j-log4j12
1.6.4
org.apache.zookeeper
zookeeper
3.4.3
jar
compile
org.apache.zookeeper
zookeeper
3.4.3
test-jar
test
org.jboss.netty
netty
3.2.4.Final
compile
org.apache.bookkeeper
bookkeeper-server
${project.parent.version}
compile
jar
org.apache.bookkeeper
bookkeeper-server
${project.parent.version}
test
test-jar
log4j
log4j
1.2.15
javax.mail
mail
javax.jms
jms
com.sun.jdmk
jmxtools
com.sun.jmx
jmxri
commons-cli
commons-cli
1.2
org.apache.hadoop
hadoop-common
0.23.1
compile
org.apache.hadoop
hadoop-hdfs
0.23.1
compile
commons-daemon
commons-daemon
bookkeeper-release-4.2.4/bookkeeper-benchmark/src/ 0000775 0000000 0000000 00000000000 12445073612 0022136 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/src/main/ 0000775 0000000 0000000 00000000000 12445073612 0023062 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/src/main/java/ 0000775 0000000 0000000 00000000000 12445073612 0024003 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/src/main/java/org/ 0000775 0000000 0000000 00000000000 12445073612 0024572 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/src/main/java/org/apache/ 0000775 0000000 0000000 00000000000 12445073612 0026013 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/src/main/java/org/apache/bookkeeper/ 0000775 0000000 0000000 00000000000 12445073612 0030141 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/src/main/java/org/apache/bookkeeper/benchmark/ 0000775 0000000 0000000 00000000000 12445073612 0032073 5 ustar 00root root 0000000 0000000 BenchBookie.java 0000664 0000000 0000000 00000020320 12445073612 0035024 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/src/main/java/org/apache/bookkeeper/benchmark /*
* 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.benchmark;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import java.io.IOException;
import org.apache.zookeeper.KeeperException;
import org.apache.bookkeeper.proto.BookieClient;
import org.apache.bookkeeper.proto.BookieProtocol;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.WriteCallback;
import org.apache.bookkeeper.util.OrderedSafeExecutor;
import org.apache.bookkeeper.client.BookKeeper;
import org.apache.bookkeeper.client.BKException;
import org.apache.bookkeeper.client.LedgerHandle;
import org.apache.bookkeeper.conf.ClientConfiguration;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.socket.ClientSocketChannelFactory;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.cli.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BenchBookie {
static Logger LOG = LoggerFactory.getLogger(BenchBookie.class);
static class LatencyCallback implements WriteCallback {
boolean complete;
@Override
synchronized public void writeComplete(int rc, long ledgerId, long entryId,
InetSocketAddress addr, Object ctx) {
if (rc != 0) {
LOG.error("Got error " + rc);
}
complete = true;
notifyAll();
}
synchronized public void resetComplete() {
complete = false;
}
synchronized public void waitForComplete() throws InterruptedException {
while(!complete) {
wait();
}
}
}
static class ThroughputCallback implements WriteCallback {
int count;
int waitingCount = Integer.MAX_VALUE;
synchronized public void writeComplete(int rc, long ledgerId, long entryId,
InetSocketAddress addr, Object ctx) {
if (rc != 0) {
LOG.error("Got error " + rc);
}
count++;
if (count >= waitingCount) {
notifyAll();
}
}
synchronized public void waitFor(int count) throws InterruptedException {
while(this.count < count) {
waitingCount = count;
wait(1000);
}
waitingCount = Integer.MAX_VALUE;
}
}
private static long getValidLedgerId(String zkServers)
throws IOException, BKException, KeeperException, InterruptedException {
BookKeeper bkc = null;
LedgerHandle lh = null;
long id = 0;
try {
bkc =new BookKeeper(zkServers);
lh = bkc.createLedger(1, 1, BookKeeper.DigestType.CRC32,
new byte[20]);
id = lh.getId();
return id;
} finally {
if (lh != null) { lh.close(); }
if (bkc != null) { bkc.close(); }
}
}
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args)
throws InterruptedException, ParseException, IOException,
BKException, KeeperException {
Options options = new Options();
options.addOption("host", true, "Hostname or IP of bookie to benchmark");
options.addOption("port", true, "Port of bookie to benchmark (default 3181)");
options.addOption("zookeeper", true, "Zookeeper ensemble, (default \"localhost:2181\")");
options.addOption("size", true, "Size of message to send, in bytes (default 1024)");
options.addOption("help", false, "This message");
CommandLineParser parser = new PosixParser();
CommandLine cmd = parser.parse(options, args);
if (cmd.hasOption("help") || !cmd.hasOption("host")) {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("BenchBookie ", options);
System.exit(-1);
}
String addr = cmd.getOptionValue("host");
int port = Integer.valueOf(cmd.getOptionValue("port", "3181"));
int size = Integer.valueOf(cmd.getOptionValue("size", "1024"));
String servers = cmd.getOptionValue("zookeeper", "localhost:2181");
ClientSocketChannelFactory channelFactory
= new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors
.newCachedThreadPool());
OrderedSafeExecutor executor = new OrderedSafeExecutor(1);
ClientConfiguration conf = new ClientConfiguration();
BookieClient bc = new BookieClient(conf, channelFactory, executor);
LatencyCallback lc = new LatencyCallback();
ThroughputCallback tc = new ThroughputCallback();
int warmUpCount = 999;
long ledger = getValidLedgerId(servers);
for(long entry = 0; entry < warmUpCount; entry++) {
ChannelBuffer toSend = ChannelBuffers.buffer(size);
toSend.resetReaderIndex();
toSend.resetWriterIndex();
toSend.writeLong(ledger);
toSend.writeLong(entry);
toSend.writerIndex(toSend.capacity());
bc.addEntry(new InetSocketAddress(addr, port), ledger, new byte[20],
entry, toSend, tc, null, BookieProtocol.FLAG_NONE);
}
LOG.info("Waiting for warmup");
tc.waitFor(warmUpCount);
ledger = getValidLedgerId(servers);
LOG.info("Benchmarking latency");
int entryCount = 5000;
long startTime = System.nanoTime();
for(long entry = 0; entry < entryCount; entry++) {
ChannelBuffer toSend = ChannelBuffers.buffer(size);
toSend.resetReaderIndex();
toSend.resetWriterIndex();
toSend.writeLong(ledger);
toSend.writeLong(entry);
toSend.writerIndex(toSend.capacity());
lc.resetComplete();
bc.addEntry(new InetSocketAddress(addr, port), ledger, new byte[20],
entry, toSend, lc, null, BookieProtocol.FLAG_NONE);
lc.waitForComplete();
}
long endTime = System.nanoTime();
LOG.info("Latency: " + (((double)(endTime-startTime))/((double)entryCount))/1000000.0);
entryCount = 50000;
ledger = getValidLedgerId(servers);
LOG.info("Benchmarking throughput");
startTime = System.currentTimeMillis();
tc = new ThroughputCallback();
for(long entry = 0; entry < entryCount; entry++) {
ChannelBuffer toSend = ChannelBuffers.buffer(size);
toSend.resetReaderIndex();
toSend.resetWriterIndex();
toSend.writeLong(ledger);
toSend.writeLong(entry);
toSend.writerIndex(toSend.capacity());
bc.addEntry(new InetSocketAddress(addr, port), ledger, new byte[20],
entry, toSend, tc, null, BookieProtocol.FLAG_NONE);
}
tc.waitFor(entryCount);
endTime = System.currentTimeMillis();
LOG.info("Throughput: " + ((long)entryCount)*1000/(endTime-startTime));
bc.close();
channelFactory.releaseExternalResources();
executor.shutdown();
}
}
BenchReadThroughputLatency.java 0000664 0000000 0000000 00000026340 12445073612 0040111 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/src/main/java/org/apache/bookkeeper/benchmark /*
* 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.benchmark;
import org.apache.bookkeeper.client.BKException;
import org.apache.bookkeeper.conf.ClientConfiguration;
import org.apache.bookkeeper.client.BookKeeper;
import org.apache.bookkeeper.client.LedgerHandle;
import org.apache.bookkeeper.client.LedgerEntry;
import org.apache.bookkeeper.client.AsyncCallback.AddCallback;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher.Event;
import java.util.Enumeration;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.ArrayList;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.cli.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BenchReadThroughputLatency {
static Logger LOG = LoggerFactory.getLogger(BenchReadThroughputLatency.class);
private static final Pattern LEDGER_PATTERN = Pattern.compile("L([0-9]+)$");
private static final Comparator ZK_LEDGER_COMPARE = new Comparator() {
public int compare(String o1, String o2) {
try {
Matcher m1 = LEDGER_PATTERN.matcher(o1);
Matcher m2 = LEDGER_PATTERN.matcher(o2);
if (m1.find() && m2.find()) {
return Integer.valueOf(m1.group(1))
- Integer.valueOf(m2.group(1));
} else {
return o1.compareTo(o2);
}
} catch (Throwable t) {
return o1.compareTo(o2);
}
}
};
private static void readLedger(ClientConfiguration conf, long ledgerId, byte[] passwd) {
LOG.info("Reading ledger {}", ledgerId);
BookKeeper bk = null;
long time = 0;
long entriesRead = 0;
long lastRead = 0;
int nochange = 0;
long absoluteLimit = 5000000;
LedgerHandle lh = null;
try {
bk = new BookKeeper(conf);
while (true) {
lh = bk.openLedgerNoRecovery(ledgerId, BookKeeper.DigestType.CRC32,
passwd);
long lastConfirmed = Math.min(lh.getLastAddConfirmed(), absoluteLimit);
if (lastConfirmed == lastRead) {
nochange++;
if (nochange == 10) {
break;
} else {
Thread.sleep(1000);
continue;
}
} else {
nochange = 0;
}
long starttime = System.nanoTime();
while (lastRead < lastConfirmed) {
long nextLimit = lastRead + 100000;
long readTo = Math.min(nextLimit, lastConfirmed);
Enumeration entries = lh.readEntries(lastRead+1, readTo);
lastRead = readTo;
while (entries.hasMoreElements()) {
LedgerEntry e = entries.nextElement();
entriesRead++;
if ((entriesRead % 10000) == 0) {
LOG.info("{} entries read", entriesRead);
}
}
}
long endtime = System.nanoTime();
time += endtime - starttime;
lh.close();
lh = null;
Thread.sleep(1000);
}
} catch (InterruptedException ie) {
// ignore
} catch (Exception e ) {
LOG.error("Exception in reader", e);
} finally {
LOG.info("Read {} in {}ms", entriesRead, time/1000/1000);
try {
if (lh != null) {
lh.close();
}
if (bk != null) {
bk.close();
}
} catch (Exception e) {
LOG.error("Exception closing stuff", e);
}
}
}
private static void usage(Options options) {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("BenchReadThroughputLatency ", options);
}
@SuppressWarnings("deprecation")
public static void main(String[] args) throws Exception {
Options options = new Options();
options.addOption("ledger", true, "Ledger to read. If empty, read all ledgers which come available. "
+ " Cannot be used with -listen");
options.addOption("listen", true, "Listen for creation of ledgers, and read each one fully");
options.addOption("password", true, "Password used to access ledgers (default 'benchPasswd')");
options.addOption("zookeeper", true, "Zookeeper ensemble, default \"localhost:2181\"");
options.addOption("sockettimeout", true, "Socket timeout for bookkeeper client. In seconds. Default 5");
options.addOption("help", false, "This message");
CommandLineParser parser = new PosixParser();
CommandLine cmd = parser.parse(options, args);
if (cmd.hasOption("help")) {
usage(options);
System.exit(-1);
}
final String servers = cmd.getOptionValue("zookeeper", "localhost:2181");
final byte[] passwd = cmd.getOptionValue("password", "benchPasswd").getBytes();
final int sockTimeout = Integer.valueOf(cmd.getOptionValue("sockettimeout", "5"));
if (cmd.hasOption("ledger") && cmd.hasOption("listen")) {
LOG.error("Cannot used -ledger and -listen together");
usage(options);
System.exit(-1);
}
final AtomicInteger ledger = new AtomicInteger(0);
final AtomicInteger numLedgers = new AtomicInteger(0);
if (cmd.hasOption("ledger")) {
ledger.set(Integer.valueOf(cmd.getOptionValue("ledger")));
} else if (cmd.hasOption("listen")) {
numLedgers.set(Integer.valueOf(cmd.getOptionValue("listen")));
} else {
LOG.error("You must use -ledger or -listen");
usage(options);
System.exit(-1);
}
final CountDownLatch shutdownLatch = new CountDownLatch(1);
final CountDownLatch connectedLatch = new CountDownLatch(1);
final String nodepath = String.format("/ledgers/L%010d", ledger.get());
final ClientConfiguration conf = new ClientConfiguration();
conf.setReadTimeout(sockTimeout).setZkServers(servers);
final ZooKeeper zk = new ZooKeeper(servers, 3000, new Watcher() {
public void process(WatchedEvent event) {
if (event.getState() == Event.KeeperState.SyncConnected
&& event.getType() == Event.EventType.None) {
connectedLatch.countDown();
}
}
});
try {
zk.register(new Watcher() {
public void process(WatchedEvent event) {
try {
if (event.getState() == Event.KeeperState.SyncConnected
&& event.getType() == Event.EventType.None) {
connectedLatch.countDown();
} else if (event.getType() == Event.EventType.NodeCreated
&& event.getPath().equals(nodepath)) {
readLedger(conf, ledger.get(), passwd);
shutdownLatch.countDown();
} else if (event.getType() == Event.EventType.NodeChildrenChanged) {
if (numLedgers.get() < 0) {
return;
}
List children = zk.getChildren("/ledgers", true);
List ledgers = new ArrayList();
for (String child : children) {
if (LEDGER_PATTERN.matcher(child).find()) {
ledgers.add(child);
}
}
Collections.sort(ledgers, ZK_LEDGER_COMPARE);
String last = ledgers.get(ledgers.size() - 1);
final Matcher m = LEDGER_PATTERN.matcher(last);
if (m.find()) {
int ledgersLeft = numLedgers.decrementAndGet();
Thread t = new Thread() {
public void run() {
readLedger(conf, Long.valueOf(m.group(1)), passwd);
}
};
t.start();
if (ledgersLeft <= 0) {
shutdownLatch.countDown();
}
} else {
LOG.error("Cant file ledger id in {}", last);
}
} else {
LOG.warn("Unknown event {}", event);
}
} catch (Exception e) {
LOG.error("Exception in watcher", e);
}
}
});
connectedLatch.await();
if (ledger.get() != 0) {
if (zk.exists(nodepath, true) != null) {
readLedger(conf, ledger.get(), passwd);
shutdownLatch.countDown();
} else {
LOG.info("Watching for creation of" + nodepath);
}
} else {
zk.getChildren("/ledgers", true);
}
shutdownLatch.await();
LOG.info("Shutting down");
} finally {
zk.close();
}
}
} BenchThroughputLatency.java 0000664 0000000 0000000 00000042011 12445073612 0037306 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/src/main/java/org/apache/bookkeeper/benchmark /*
* 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.benchmark;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.Timer;
import java.util.TimerTask;
import org.apache.bookkeeper.client.BKException;
import org.apache.bookkeeper.client.BookKeeper;
import org.apache.bookkeeper.client.LedgerHandle;
import org.apache.bookkeeper.client.AsyncCallback.AddCallback;
import org.apache.bookkeeper.conf.ClientConfiguration;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BenchThroughputLatency implements AddCallback, Runnable {
static Logger LOG = LoggerFactory.getLogger(BenchThroughputLatency.class);
BookKeeper bk;
LedgerHandle lh[];
AtomicLong counter;
Semaphore sem;
int numberOfLedgers = 1;
final int sendLimit;
final long latencies[];
static class Context {
long localStartTime;
long id;
Context(long id, long time){
this.id = id;
this.localStartTime = time;
}
}
public BenchThroughputLatency(int ensemble, int writeQuorumSize, int ackQuorumSize, byte[] passwd,
int numberOfLedgers, int sendLimit, ClientConfiguration conf)
throws KeeperException, IOException, InterruptedException {
this.sem = new Semaphore(conf.getThrottleValue());
bk = new BookKeeper(conf);
this.counter = new AtomicLong(0);
this.numberOfLedgers = numberOfLedgers;
this.sendLimit = sendLimit;
this.latencies = new long[sendLimit];
try{
lh = new LedgerHandle[this.numberOfLedgers];
for(int i = 0; i < this.numberOfLedgers; i++) {
lh[i] = bk.createLedger(ensemble, writeQuorumSize,
ackQuorumSize,
BookKeeper.DigestType.CRC32,
passwd);
LOG.debug("Ledger Handle: " + lh[i].getId());
}
} catch (BKException e) {
e.printStackTrace();
}
}
Random rand = new Random();
public void close() throws InterruptedException, BKException {
for(int i = 0; i < numberOfLedgers; i++) {
lh[i].close();
}
bk.close();
}
long previous = 0;
byte bytes[];
void setEntryData(byte data[]) {
bytes = data;
}
int lastLedger = 0;
private int getRandomLedger() {
return rand.nextInt(numberOfLedgers);
}
int latencyIndex = -1;
AtomicLong completedRequests = new AtomicLong(0);
long duration = -1;
synchronized public long getDuration() {
return duration;
}
public void run() {
LOG.info("Running...");
long start = previous = System.currentTimeMillis();
int sent = 0;
Thread reporter = new Thread() {
public void run() {
try {
while(true) {
Thread.sleep(1000);
LOG.info("ms: {} req: {}", System.currentTimeMillis(), completedRequests.getAndSet(0));
}
} catch (InterruptedException ie) {
LOG.info("Caught interrupted exception, going away");
}
}
};
reporter.start();
long beforeSend = System.nanoTime();
while(!Thread.currentThread().isInterrupted() && sent < sendLimit) {
try {
sem.acquire();
if (sent == 10000) {
long afterSend = System.nanoTime();
long time = afterSend - beforeSend;
LOG.info("Time to send first batch: {}s {}ns ",
time/1000/1000/1000, time);
}
} catch (InterruptedException e) {
break;
}
final int index = getRandomLedger();
LedgerHandle h = lh[index];
if (h == null) {
LOG.error("Handle " + index + " is null!");
} else {
long nanoTime = System.nanoTime();
lh[index].asyncAddEntry(bytes, this, new Context(sent, nanoTime));
counter.incrementAndGet();
}
sent++;
}
LOG.info("Sent: " + sent);
try {
int i = 0;
while(this.counter.get() > 0) {
Thread.sleep(1000);
i++;
if (i > 30) {
break;
}
}
} catch(InterruptedException e) {
LOG.error("Interrupted while waiting", e);
}
synchronized(this) {
duration = System.currentTimeMillis() - start;
}
throughput = sent*1000/getDuration();
reporter.interrupt();
try {
reporter.join();
} catch (InterruptedException ie) {
// ignore
}
LOG.info("Finished processing in ms: " + getDuration() + " tp = " + throughput);
}
long throughput = -1;
public long getThroughput() {
return throughput;
}
long threshold = 20000;
long runningAverageCounter = 0;
long totalTime = 0;
@Override
public void addComplete(int rc, LedgerHandle lh, long entryId, Object ctx) {
Context context = (Context) ctx;
// we need to use the id passed in the context in the case of
// multiple ledgers, and it works even with one ledger
entryId = context.id;
long newTime = System.nanoTime() - context.localStartTime;
sem.release();
counter.decrementAndGet();
if (rc == 0) {
latencies[(int)entryId] = newTime;
completedRequests.incrementAndGet();
}
}
@SuppressWarnings("deprecation")
public static void main(String[] args)
throws KeeperException, IOException, InterruptedException, ParseException, BKException {
Options options = new Options();
options.addOption("time", true, "Running time (seconds), default 60");
options.addOption("entrysize", true, "Entry size (bytes), default 1024");
options.addOption("ensemble", true, "Ensemble size, default 3");
options.addOption("quorum", true, "Quorum size, default 2");
options.addOption("ackQuorum", true, "Ack quorum size, default is same as quorum");
options.addOption("throttle", true, "Max outstanding requests, default 10000");
options.addOption("ledgers", true, "Number of ledgers, default 1");
options.addOption("zookeeper", true, "Zookeeper ensemble, default \"localhost:2181\"");
options.addOption("password", true, "Password used to create ledgers (default 'benchPasswd')");
options.addOption("coordnode", true, "Coordination znode for multi client benchmarks (optional)");
options.addOption("timeout", true, "Number of seconds after which to give up");
options.addOption("sockettimeout", true, "Socket timeout for bookkeeper client. In seconds. Default 5");
options.addOption("skipwarmup", false, "Skip warm up, default false");
options.addOption("sendlimit", true, "Max number of entries to send. Default 20000000");
options.addOption("latencyFile", true, "File to dump latencies. Default is latencyDump.dat");
options.addOption("help", false, "This message");
CommandLineParser parser = new PosixParser();
CommandLine cmd = parser.parse(options, args);
if (cmd.hasOption("help")) {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("BenchThroughputLatency ", options);
System.exit(-1);
}
long runningTime = Long.valueOf(cmd.getOptionValue("time", "60"));
String servers = cmd.getOptionValue("zookeeper", "localhost:2181");
int entrysize = Integer.valueOf(cmd.getOptionValue("entrysize", "1024"));
int ledgers = Integer.valueOf(cmd.getOptionValue("ledgers", "1"));
int ensemble = Integer.valueOf(cmd.getOptionValue("ensemble", "3"));
int quorum = Integer.valueOf(cmd.getOptionValue("quorum", "2"));
int ackQuorum = quorum;
if (cmd.hasOption("ackQuorum")) {
ackQuorum = Integer.valueOf(cmd.getOptionValue("ackQuorum"));
}
int throttle = Integer.valueOf(cmd.getOptionValue("throttle", "10000"));
int sendLimit = Integer.valueOf(cmd.getOptionValue("sendlimit", "20000000"));
final int sockTimeout = Integer.valueOf(cmd.getOptionValue("sockettimeout", "5"));
String coordinationZnode = cmd.getOptionValue("coordnode");
final byte[] passwd = cmd.getOptionValue("password", "benchPasswd").getBytes();
String latencyFile = cmd.getOptionValue("latencyFile", "latencyDump.dat");
Timer timeouter = new Timer();
if (cmd.hasOption("timeout")) {
final long timeout = Long.valueOf(cmd.getOptionValue("timeout", "360")) * 1000;
timeouter.schedule(new TimerTask() {
public void run() {
System.err.println("Timing out benchmark after " + timeout + "ms");
System.exit(-1);
}
}, timeout);
}
LOG.warn("(Parameters received) running time: " + runningTime +
", entry size: " + entrysize + ", ensemble size: " + ensemble +
", quorum size: " + quorum +
", throttle: " + throttle +
", number of ledgers: " + ledgers +
", zk servers: " + servers +
", latency file: " + latencyFile);
long totalTime = runningTime*1000;
// Do a warmup run
Thread thread;
byte data[] = new byte[entrysize];
Arrays.fill(data, (byte)'x');
ClientConfiguration conf = new ClientConfiguration();
conf.setThrottleValue(throttle).setReadTimeout(sockTimeout).setZkServers(servers);
if (!cmd.hasOption("skipwarmup")) {
long throughput;
LOG.info("Starting warmup");
throughput = warmUp(data, ledgers, ensemble, quorum, passwd, conf);
LOG.info("Warmup tp: " + throughput);
LOG.info("Warmup phase finished");
}
// Now do the benchmark
BenchThroughputLatency bench = new BenchThroughputLatency(ensemble, quorum, ackQuorum,
passwd, ledgers, sendLimit, conf);
bench.setEntryData(data);
thread = new Thread(bench);
ZooKeeper zk = null;
if (coordinationZnode != null) {
final CountDownLatch connectLatch = new CountDownLatch(1);
zk = new ZooKeeper(servers, 15000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getState() == KeeperState.SyncConnected) {
connectLatch.countDown();
}
}});
if (!connectLatch.await(10, TimeUnit.SECONDS)) {
LOG.error("Couldn't connect to zookeeper at " + servers);
zk.close();
System.exit(-1);
}
final CountDownLatch latch = new CountDownLatch(1);
LOG.info("Waiting for " + coordinationZnode);
if (zk.exists(coordinationZnode, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getType() == EventType.NodeCreated) {
latch.countDown();
}
}}) != null) {
latch.countDown();
}
latch.await();
LOG.info("Coordination znode created");
}
thread.start();
Thread.sleep(totalTime);
thread.interrupt();
thread.join();
LOG.info("Calculating percentiles");
int numlat = 0;
for(int i = 0; i < bench.latencies.length; i++) {
if (bench.latencies[i] > 0) {
numlat++;
}
}
int numcompletions = numlat;
numlat = Math.min(bench.sendLimit, numlat);
long[] latency = new long[numlat];
int j =0;
for(int i = 0; i < bench.latencies.length && j < numlat; i++) {
if (bench.latencies[i] > 0) {
latency[j++] = bench.latencies[i];
}
}
Arrays.sort(latency);
long tp = (long)((double)(numcompletions*1000.0)/(double)bench.getDuration());
LOG.info(numcompletions + " completions in " + bench.getDuration() + " seconds: " + tp + " ops/sec");
if (zk != null) {
zk.create(coordinationZnode + "/worker-",
("tp " + tp + " duration " + bench.getDuration()).getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
zk.close();
}
// dump the latencies for later debugging (it will be sorted by entryid)
OutputStream fos = new BufferedOutputStream(new FileOutputStream(latencyFile));
for(Long l: latency) {
fos.write((Long.toString(l)+"\t"+(l/1000000)+ "ms\n").getBytes());
}
fos.flush();
fos.close();
// now get the latencies
LOG.info("99th percentile latency: {}", percentile(latency, 99));
LOG.info("95th percentile latency: {}", percentile(latency, 95));
bench.close();
timeouter.cancel();
}
private static double percentile(long[] latency, int percentile) {
int size = latency.length;
int sampleSize = (size * percentile) / 100;
long total = 0;
int count = 0;
for(int i = 0; i < sampleSize; i++) {
total += latency[i];
count++;
}
return ((double)total/(double)count)/1000000.0;
}
private static long warmUp(byte[] data, int ledgers, int ensemble, int qSize,
byte[] passwd, ClientConfiguration conf)
throws KeeperException, IOException, InterruptedException, BKException {
final CountDownLatch connectLatch = new CountDownLatch(1);
final int bookies;
String bookieRegistrationPath = conf.getZkAvailableBookiesPath();
ZooKeeper zk = null;
try {
final String servers = conf.getZkServers();
zk = new ZooKeeper(servers, 15000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getState() == KeeperState.SyncConnected) {
connectLatch.countDown();
}
}});
if (!connectLatch.await(10, TimeUnit.SECONDS)) {
LOG.error("Couldn't connect to zookeeper at " + servers);
throw new IOException("Couldn't connect to zookeeper " + servers);
}
bookies = zk.getChildren(bookieRegistrationPath, false).size();
} finally {
if (zk != null) {
zk.close();
}
}
BenchThroughputLatency warmup = new BenchThroughputLatency(bookies, bookies, bookies, passwd,
ledgers, 10000, conf);
warmup.setEntryData(data);
Thread thread = new Thread(warmup);
thread.start();
thread.join();
warmup.close();
return warmup.getThroughput();
}
}
MySqlClient.java 0000664 0000000 0000000 00000012002 12445073612 0035056 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/src/main/java/org/apache/bookkeeper/benchmark package org.apache.bookkeeper.benchmark;
/**
* 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.io.FileOutputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import org.apache.bookkeeper.client.BookKeeper;
import org.apache.bookkeeper.client.LedgerHandle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.zookeeper.KeeperException;
public class MySqlClient {
static Logger LOG = LoggerFactory.getLogger(MySqlClient.class);
BookKeeper x;
LedgerHandle lh;
Integer entryId;
HashMap map;
FileOutputStream fStream;
FileOutputStream fStreamLocal;
long start, lastId;
Connection con;
Statement stmt;
public MySqlClient(String hostport, String user, String pass)
throws ClassNotFoundException {
entryId = 0;
map = new HashMap();
Class.forName("com.mysql.jdbc.Driver");
// database is named "bookkeeper"
String url = "jdbc:mysql://" + hostport + "/bookkeeper";
try {
con = DriverManager.getConnection(url, user, pass);
stmt = con.createStatement();
// drop table and recreate it
stmt.execute("DROP TABLE IF EXISTS data;");
stmt.execute("create table data(transaction_id bigint PRIMARY KEY AUTO_INCREMENT, content TEXT);");
LOG.info("Database initialization terminated");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void closeHandle() throws KeeperException, InterruptedException, SQLException {
con.close();
}
/**
* First parameter is an integer defining the length of the message
* Second parameter is the number of writes
* Third parameter is host:port
* Fourth parameter is username
* Fifth parameter is password
* @param args
* @throws ClassNotFoundException
* @throws SQLException
*/
public static void main(String[] args) throws ClassNotFoundException, SQLException {
int lenght = Integer.parseInt(args[1]);
StringBuilder sb = new StringBuilder();
while(lenght-- > 0) {
sb.append('a');
}
try {
MySqlClient c = new MySqlClient(args[2], args[3], args[4]);
c.writeSameEntryBatch(sb.toString().getBytes(), Integer.parseInt(args[0]));
c.writeSameEntry(sb.toString().getBytes(), Integer.parseInt(args[0]));
c.closeHandle();
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
/**
* Adds data entry to the DB
* @param data the entry to be written, given as a byte array
* @param times the number of times the entry should be written on the DB */
void writeSameEntryBatch(byte[] data, int times) throws InterruptedException, SQLException {
start = System.currentTimeMillis();
int count = times;
String content = new String(data);
System.out.println("Data: " + content + ", " + data.length);
while(count-- > 0) {
stmt.addBatch("insert into data(content) values(\"" + content + "\");");
}
LOG.info("Finished writing batch SQL command in ms: " + (System.currentTimeMillis() - start));
start = System.currentTimeMillis();
stmt.executeBatch();
System.out.println("Finished " + times + " writes in ms: " + (System.currentTimeMillis() - start));
LOG.info("Ended computation");
}
void writeSameEntry(byte[] data, int times) throws InterruptedException, SQLException {
start = System.currentTimeMillis();
int count = times;
String content = new String(data);
System.out.println("Data: " + content + ", " + data.length);
while(count-- > 0) {
stmt.executeUpdate("insert into data(content) values(\"" + content + "\");");
}
System.out.println("Finished " + times + " writes in ms: " + (System.currentTimeMillis() - start));
LOG.info("Ended computation");
}
}
TestClient.java 0000664 0000000 0000000 00000034375 12445073612 0034751 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/src/main/java/org/apache/bookkeeper/benchmark /*
*
* 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.benchmark;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.bookkeeper.client.BKException;
import org.apache.bookkeeper.client.BookKeeper;
import org.apache.bookkeeper.client.LedgerHandle;
import org.apache.bookkeeper.client.AsyncCallback.AddCallback;
import org.apache.bookkeeper.client.BookKeeper.DigestType;
import org.apache.bookkeeper.conf.ClientConfiguration;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is a simple test program to compare the performance of writing to
* BookKeeper and to the local file system.
*
*/
public class TestClient {
private static final Logger LOG = LoggerFactory.getLogger(TestClient.class);
/**
* First says if entries should be written to BookKeeper (0) or to the local
* disk (1). Second parameter is an integer defining the length of a ledger entry.
* Third parameter is the number of writes.
*
* @param args
*/
public static void main(String[] args) throws ParseException {
Options options = new Options();
options.addOption("length", true, "Length of packets being written. Default 1024");
options.addOption("target", true, "Target medium to write to. Options are bk, fs & hdfs. Default fs");
options.addOption("runfor", true, "Number of seconds to run for. Default 60");
options.addOption("path", true, "Path to write to. fs & hdfs only. Default /foobar");
options.addOption("zkservers", true, "ZooKeeper servers, comma separated. bk only. Default localhost:2181.");
options.addOption("bkensemble", true, "BookKeeper ledger ensemble size. bk only. Default 3");
options.addOption("bkquorum", true, "BookKeeper ledger quorum size. bk only. Default 2");
options.addOption("bkthrottle", true, "BookKeeper throttle size. bk only. Default 10000");
options.addOption("sync", false, "Use synchronous writes with BookKeeper. bk only.");
options.addOption("numconcurrent", true, "Number of concurrently clients. Default 1");
options.addOption("timeout", true, "Number of seconds after which to give up");
options.addOption("help", false, "This message");
CommandLineParser parser = new PosixParser();
CommandLine cmd = parser.parse(options, args);
if (cmd.hasOption("help")) {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("TestClient ", options);
System.exit(-1);
}
int length = Integer.valueOf(cmd.getOptionValue("length", "1024"));
String target = cmd.getOptionValue("target", "fs");
long runfor = Long.valueOf(cmd.getOptionValue("runfor", "60")) * 1000;
StringBuilder sb = new StringBuilder();
while(length-- > 0) {
sb.append('a');
}
Timer timeouter = new Timer();
if (cmd.hasOption("timeout")) {
final long timeout = Long.valueOf(cmd.getOptionValue("timeout", "360")) * 1000;
timeouter.schedule(new TimerTask() {
public void run() {
System.err.println("Timing out benchmark after " + timeout + "ms");
System.exit(-1);
}
}, timeout);
}
BookKeeper bkc = null;
try {
int numFiles = Integer.valueOf(cmd.getOptionValue("numconcurrent", "1"));
int numThreads = Math.min(numFiles, 1000);
byte[] data = sb.toString().getBytes();
long runid = System.currentTimeMillis();
List> clients = new ArrayList>();
if (target.equals("bk")) {
String zkservers = cmd.getOptionValue("zkservers", "localhost:2181");
int bkensemble = Integer.valueOf(cmd.getOptionValue("bkensemble", "3"));
int bkquorum = Integer.valueOf(cmd.getOptionValue("bkquorum", "2"));
int bkthrottle = Integer.valueOf(cmd.getOptionValue("bkthrottle", "10000"));
ClientConfiguration conf = new ClientConfiguration();
conf.setThrottleValue(bkthrottle);
conf.setZkServers(zkservers);
bkc = new BookKeeper(conf);
List handles = new ArrayList();
for (int i = 0; i < numFiles; i++) {
handles.add(bkc.createLedger(bkensemble, bkquorum, DigestType.CRC32, new byte[] {'a', 'b'}));
}
for (int i = 0; i < numFiles; i++) {
clients.add(new BKClient(handles, data, runfor, cmd.hasOption("sync")));
}
} else if (target.equals("hdfs")) {
FileSystem fs = FileSystem.get(new Configuration());
LOG.info("Default replication for HDFS: {}", fs.getDefaultReplication());
List streams = new ArrayList();
for (int i = 0; i < numFiles; i++) {
String path = cmd.getOptionValue("path", "/foobar");
streams.add(fs.create(new Path(path + runid + "_" + i)));
}
for (int i = 0; i < numThreads; i++) {
clients.add(new HDFSClient(streams, data, runfor));
}
} else if (target.equals("fs")) {
List streams = new ArrayList();
for (int i = 0; i < numFiles; i++) {
String path = cmd.getOptionValue("path", "/foobar " + i);
streams.add(new FileOutputStream(path + runid + "_" + i));
}
for (int i = 0; i < numThreads; i++) {
clients.add(new FileClient(streams, data, runfor));
}
} else {
LOG.error("Unknown option: " + target);
throw new IllegalArgumentException("Unknown target " + target);
}
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
long start = System.currentTimeMillis();
List> results = executor.invokeAll(clients,
10, TimeUnit.MINUTES);
long end = System.currentTimeMillis();
long count = 0;
for (Future r : results) {
if (!r.isDone()) {
LOG.warn("Job didn't complete");
System.exit(2);
}
long c = r.get();
if (c == 0) {
LOG.warn("Task didn't complete");
}
count += c;
}
long time = end-start;
LOG.info("Finished processing writes (ms): {} TPT: {} op/s",
time, count/((double)time/1000));
executor.shutdown();
} catch (ExecutionException ee) {
LOG.error("Exception in worker", ee);
} catch (KeeperException ke) {
LOG.error("Error accessing zookeeper", ke);
} catch (BKException e) {
LOG.error("Error accessing bookkeeper", e);
} catch (IOException ioe) {
LOG.error("I/O exception during benchmark", ioe);
} catch (InterruptedException ie) {
LOG.error("Benchmark interrupted", ie);
} finally {
if (bkc != null) {
try {
bkc.close();
} catch (BKException bke) {
LOG.error("Error closing bookkeeper client", bke);
} catch (InterruptedException ie) {
LOG.warn("Interrupted closing bookkeeper client", ie);
}
}
}
timeouter.cancel();
}
static class HDFSClient implements Callable {
final List streams;
final byte[] data;
final long time;
final Random r;
HDFSClient(List streams, byte[] data, long time) {
this.streams = streams;
this.data = data;
this.time = time;
this.r = new Random(System.identityHashCode(this));
}
public Long call() {
try {
long count = 0;
long start = System.currentTimeMillis();
long stopat = start + time;
while(System.currentTimeMillis() < stopat) {
FSDataOutputStream stream = streams.get(r.nextInt(streams.size()));
synchronized(stream) {
stream.write(data);
stream.flush();
stream.hflush();
}
count++;
}
long time = (System.currentTimeMillis() - start);
LOG.info("Worker finished processing writes (ms): {} TPT: {} op/s",
time, count/((double)time/1000));
return count;
} catch(IOException ioe) {
LOG.error("Exception in worker thread", ioe);
return 0L;
}
}
}
static class FileClient implements Callable {
final List streams;
final byte[] data;
final long time;
final Random r;
FileClient(List streams, byte[] data, long time) {
this.streams = streams;
this.data = data;
this.time = time;
this.r = new Random(System.identityHashCode(this));
}
public Long call() {
try {
long count = 0;
long start = System.currentTimeMillis();
long stopat = start + time;
while(System.currentTimeMillis() < stopat) {
FileOutputStream stream = streams.get(r.nextInt(streams.size()));
synchronized(stream) {
stream.write(data);
stream.flush();
stream.getChannel().force(false);
}
count++;
}
long time = (System.currentTimeMillis() - start);
LOG.info("Worker finished processing writes (ms): {} TPT: {} op/s", time, count/((double)time/1000));
return count;
} catch(IOException ioe) {
LOG.error("Exception in worker thread", ioe);
return 0L;
}
}
}
static class BKClient implements Callable, AddCallback {
final List handles;
final byte[] data;
final long time;
final Random r;
final boolean sync;
final AtomicLong success = new AtomicLong(0);
final AtomicLong outstanding = new AtomicLong(0);
BKClient(List handles, byte[] data, long time, boolean sync) {
this.handles = handles;
this.data = data;
this.time = time;
this.r = new Random(System.identityHashCode(this));
this.sync = sync;
}
public Long call() {
try {
long start = System.currentTimeMillis();
long stopat = start + time;
while(System.currentTimeMillis() < stopat) {
LedgerHandle lh = handles.get(r.nextInt(handles.size()));
if (sync) {
lh.addEntry(data);
success.incrementAndGet();
} else {
lh.asyncAddEntry(data, this, null);
outstanding.incrementAndGet();
}
}
int ticks = 10; // don't wait for more than 10 seconds
while (outstanding.get() > 0 && ticks-- > 0) {
Thread.sleep(10);
}
long time = (System.currentTimeMillis() - start);
LOG.info("Worker finished processing writes (ms): {} TPT: {} op/s",
time, success.get()/((double)time/1000));
return success.get();
} catch (BKException e) {
LOG.error("Exception in worker thread", e);
return 0L;
} catch (InterruptedException ie) {
LOG.error("Exception in worker thread", ie);
return 0L;
}
}
@Override
public void addComplete(int rc, LedgerHandle lh, long entryId, Object ctx) {
if (rc == BKException.Code.OK) {
success.incrementAndGet();
}
outstanding.decrementAndGet();
}
}
}
bookkeeper-release-4.2.4/bookkeeper-benchmark/src/test/ 0000775 0000000 0000000 00000000000 12445073612 0023115 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/src/test/java/ 0000775 0000000 0000000 00000000000 12445073612 0024036 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/src/test/java/org/ 0000775 0000000 0000000 00000000000 12445073612 0024625 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/src/test/java/org/apache/ 0000775 0000000 0000000 00000000000 12445073612 0026046 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/src/test/java/org/apache/bookkeeper/ 0000775 0000000 0000000 00000000000 12445073612 0030174 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/src/test/java/org/apache/bookkeeper/benchmark/ 0000775 0000000 0000000 00000000000 12445073612 0032126 5 ustar 00root root 0000000 0000000 TestBenchmark.java 0000664 0000000 0000000 00000013561 12445073612 0035452 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/src/test/java/org/apache/bookkeeper/benchmark /*
* 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.benchmark;
import org.junit.BeforeClass;
import org.junit.AfterClass;
import org.junit.Test;
import org.junit.Assert;
import java.net.InetSocketAddress;
import org.apache.bookkeeper.client.BookKeeper;
import org.apache.bookkeeper.client.LedgerHandle;
import org.apache.bookkeeper.util.LocalBookKeeper;
import org.apache.bookkeeper.test.BookKeeperClusterTestCase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.Arrays;
import java.util.List;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
public class TestBenchmark extends BookKeeperClusterTestCase {
protected static final Logger LOG = LoggerFactory.getLogger(TestBenchmark.class);
public TestBenchmark() {
super(5);
}
@Test(timeout=60000)
public void testThroughputLatency() throws Exception {
String latencyFile = System.getProperty("test.latency.file", "latencyDump.dat");
BenchThroughputLatency.main(new String[] {
"--zookeeper", zkUtil.getZooKeeperConnectString(),
"--time", "10",
"--skipwarmup",
"--throttle", "1",
"--sendlimit", "10000",
"--latencyFile", latencyFile
});
}
@Test(timeout=60000)
public void testBookie() throws Exception {
InetSocketAddress bookie = getBookie(0);
BenchBookie.main(new String[] {
"--host", bookie.getHostName(),
"--port", String.valueOf(bookie.getPort()),
"--zookeeper", zkUtil.getZooKeeperConnectString()
});
}
@Test(timeout=60000)
public void testReadThroughputLatency() throws Exception {
final AtomicBoolean threwException = new AtomicBoolean(false);
Thread t = new Thread() {
public void run() {
try {
BenchReadThroughputLatency.main(new String[] {
"--zookeeper", zkUtil.getZooKeeperConnectString(),
"--listen", "10"});
} catch (Throwable t) {
LOG.error("Error reading", t);
threwException.set(true);
}
}
};
t.start();
Thread.sleep(10000);
byte data[] = new byte[1024];
Arrays.fill(data, (byte)'x');
long lastLedgerId = 0;
Assert.assertTrue("Thread should be running", t.isAlive());
for (int i = 0; i < 10; i++) {
BookKeeper bk = new BookKeeper(zkUtil.getZooKeeperConnectString());
LedgerHandle lh = bk.createLedger(BookKeeper.DigestType.CRC32, "benchPasswd".getBytes());
lastLedgerId = lh.getId();
try {
for (int j = 0; j < 100; j++) {
lh.addEntry(data);
}
} finally {
lh.close();
bk.close();
}
}
for (int i = 0; i < 60; i++) {
if (!t.isAlive()) {
break;
}
Thread.sleep(1000); // wait for 10 seconds for reading to finish
}
Assert.assertFalse("Thread should be finished", t.isAlive());
BenchReadThroughputLatency.main(new String[] {
"--zookeeper", zkUtil.getZooKeeperConnectString(),
"--ledger", String.valueOf(lastLedgerId)});
final long nextLedgerId = lastLedgerId+1;
t = new Thread() {
public void run() {
try {
BenchReadThroughputLatency.main(new String[] {
"--zookeeper", zkUtil.getZooKeeperConnectString(),
"--ledger", String.valueOf(nextLedgerId)});
} catch (Throwable t) {
LOG.error("Error reading", t);
threwException.set(true);
}
}
};
t.start();
Assert.assertTrue("Thread should be running", t.isAlive());
BookKeeper bk = new BookKeeper(zkUtil.getZooKeeperConnectString());
LedgerHandle lh = bk.createLedger(BookKeeper.DigestType.CRC32, "benchPasswd".getBytes());
try {
for (int j = 0; j < 100; j++) {
lh.addEntry(data);
}
} finally {
lh.close();
bk.close();
}
for (int i = 0; i < 60; i++) {
if (!t.isAlive()) {
break;
}
Thread.sleep(1000); // wait for 10 seconds for reading to finish
}
Assert.assertFalse("Thread should be finished", t.isAlive());
Assert.assertFalse("A thread has thrown an exception, check logs", threwException.get());
}
}
bookkeeper-release-4.2.4/bookkeeper-benchmark/src/test/resources/ 0000775 0000000 0000000 00000000000 12445073612 0025127 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-benchmark/src/test/resources/log4j.properties 0000664 0000000 0000000 00000005233 12445073612 0030267 0 ustar 00root root 0000000 0000000 #
#
# 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.
#
#
#
# Bookkeeper Logging Configuration
#
# Format is " (, )+
# DEFAULT: console appender only
log4j.rootLogger=INFO, CONSOLE
# Example with rolling log file
#log4j.rootLogger=DEBUG, CONSOLE, ROLLINGFILE
# Example with rolling log file and tracing
#log4j.rootLogger=TRACE, CONSOLE, ROLLINGFILE, TRACEFILE
log4j.logger.org.apache.zookeeper=ERROR
#
# Log INFO level and above messages to the console
#
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=INFO
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %-5p - [%t:%C{1}@%L] - %m%n
#
# Add ROLLINGFILE to rootLogger to get log file output
# Log DEBUG level and above messages to a log file
log4j.appender.ROLLINGFILE=org.apache.log4j.DailyRollingFileAppender
log4j.appender.ROLLINGFILE.Threshold=DEBUG
log4j.appender.ROLLINGFILE.File=bookkeeper-benchmark.log
log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p - [%t:%C{1}@%L] - %m%n
# Max log file size of 10MB
log4j.appender.ROLLINGFILE.MaxFileSize=10MB
# uncomment the next line to limit number of backup files
#log4j.appender.ROLLINGFILE.MaxBackupIndex=10
log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n
#
# Add TRACEFILE to rootLogger to get log file output
# Log DEBUG level and above messages to a log file
log4j.appender.TRACEFILE=org.apache.log4j.FileAppender
log4j.appender.TRACEFILE.Threshold=TRACE
log4j.appender.TRACEFILE.File=bookkeeper_trace.log
log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout
### Notice we are including log4j's NDC here (%x)
log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L][%x] - %m%n
bookkeeper-release-4.2.4/bookkeeper-server/ 0000775 0000000 0000000 00000000000 12445073612 0020723 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/bin/ 0000775 0000000 0000000 00000000000 12445073612 0021473 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/bin/bookkeeper 0000775 0000000 0000000 00000015010 12445073612 0023544 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
#
#/**
# * Copyright 2007 The Apache Software Foundation
# *
# * 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.
# */
# check if net.ipv6.bindv6only is set to 1
bindv6only=$(/sbin/sysctl -n net.ipv6.bindv6only 2> /dev/null)
if [ -n "$bindv6only" ] && [ "$bindv6only" -eq "1" ]
then
echo "Error: \"net.ipv6.bindv6only\" is set to 1 - Java networking could be broken"
echo "For more info (the following page also applies to bookkeeper): http://wiki.apache.org/hadoop/HadoopIPv6"
exit 1
fi
# See the following page for extensive details on setting
# up the JVM to accept JMX remote management:
# http://java.sun.com/javase/6/docs/technotes/guides/management/agent.html
# by default we allow local JMX connections
if [ "x$JMXLOCALONLY" = "x" ]
then
JMXLOCALONLY=false
fi
if [ "x$JMXDISABLE" = "x" ]
then
echo "JMX enabled by default" >&2
# for some reason these two options are necessary on jdk6 on Ubuntu
# accord to the docs they are not necessary, but otw jconsole cannot
# do a local attach
JMX_ARGS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=$JMXLOCALONLY"
else
echo "JMX disabled by user request" >&2
fi
BINDIR=`dirname "$0"`
BK_HOME=`cd $BINDIR/..;pwd`
DEFAULT_CONF=$BK_HOME/conf/bk_server.conf
DEFAULT_LOG_CONF=$BK_HOME/conf/log4j.properties
source $BK_HOME/conf/bkenv.sh
# Check for the java to use
if [[ -z $JAVA_HOME ]]; then
JAVA=$(which java)
if [ $? = 0 ]; then
echo "JAVA_HOME not set, using java from PATH. ($JAVA)"
else
echo "Error: JAVA_HOME not set, and no java executable found in $PATH." 1>&2
exit 1
fi
else
JAVA=$JAVA_HOME/bin/java
fi
# exclude tests jar
RELEASE_JAR=`ls $BK_HOME/bookkeeper-server-*.jar 2> /dev/null | grep -v tests | tail -1`
if [ $? == 0 ]; then
BOOKIE_JAR=$RELEASE_JAR
fi
# exclude tests jar
BUILT_JAR=`ls $BK_HOME/target/bookkeeper-server-*.jar 2> /dev/null | grep -v tests | tail -1`
if [ $? != 0 ] && [ ! -e "$BOOKIE_JAR" ]; then
echo "\nCouldn't find bookkeeper jar.";
echo "Make sure you've run 'mvn package'\n";
exit 1;
elif [ -e "$BUILT_JAR" ]; then
BOOKIE_JAR=$BUILT_JAR
fi
bookkeeper_help() {
cat <
where command is one of:
bookie Run a bookie server
autorecovery Run AutoRecovery service daemon
localbookie Run a test ensemble of bookies locally
upgrade Upgrade bookie filesystem
shell Run shell for admin commands
help This help message
or command is the full name of a class with a defined main() method.
Environment variables:
BOOKIE_LOG_CONF Log4j configuration file (default $DEFAULT_LOG_CONF)
BOOKIE_CONF Configuration file (default: $DEFAULT_CONF)
BOOKIE_EXTRA_OPTS Extra options to be passed to the jvm
BOOKIE_EXTRA_CLASSPATH Add extra paths to the bookkeeper classpath
ENTRY_FORMATTER_CLASS Entry formatter class to format entries.
These variable can also be set in conf/bkenv.sh
EOF
}
add_maven_deps_to_classpath() {
MVN="mvn"
if [ "$MAVEN_HOME" != "" ]; then
MVN=${MAVEN_HOME}/bin/mvn
fi
# Need to generate classpath from maven pom. This is costly so generate it
# and cache it. Save the file into our target dir so a mvn clean will get
# clean it up and force us create a new one.
f="${BK_HOME}/target/cached_classpath.txt"
if [ ! -f "${f}" ]
then
${MVN} -f "${BK_HOME}/pom.xml" dependency:build-classpath -Dmdep.outputFile="${f}" &> /dev/null
fi
BOOKIE_CLASSPATH=${CLASSPATH}:`cat "${f}"`
}
if [ -d "$BK_HOME/lib" ]; then
for i in $BK_HOME/lib/*.jar; do
BOOKIE_CLASSPATH=$BOOKIE_CLASSPATH:$i
done
else
add_maven_deps_to_classpath
fi
# if no args specified, show usage
if [ $# = 0 ]; then
bookkeeper_help;
exit 1;
fi
# get arguments
COMMAND=$1
shift
if [ $COMMAND == "shell" ]; then
DEFAULT_LOG_CONF=$BK_HOME/conf/log4j.shell.properties
fi
if [ -z "$BOOKIE_CONF" ]; then
BOOKIE_CONF=$DEFAULT_CONF
fi
if [ -z "$BOOKIE_LOG_CONF" ]; then
BOOKIE_LOG_CONF=$DEFAULT_LOG_CONF
fi
BOOKIE_CLASSPATH="$BOOKIE_JAR:$BOOKIE_CLASSPATH:$BOOKIE_EXTRA_CLASSPATH"
BOOKIE_CLASSPATH="`dirname $BOOKIE_LOG_CONF`:$BOOKIE_CLASSPATH"
OPTS="$OPTS -Dlog4j.configuration=`basename $BOOKIE_LOG_CONF`"
OPTS="-cp $BOOKIE_CLASSPATH $OPTS"
OPTS="$OPTS $BOOKIE_EXTRA_OPTS"
# Disable ipv6 as it can cause issues
OPTS="$OPTS -Djava.net.preferIPv4Stack=true"
# log directory & file
BOOKIE_ROOT_LOGGER=${BOOKIE_ROOT_LOGGER:-"INFO,CONSOLE"}
BOOKIE_LOG_DIR=${BOOKIE_LOG_DIR:-"$BK_HOME/logs"}
BOOKIE_LOG_FILE=${BOOKIE_LOG_FILE:-"bookkeeper-server.log"}
#Configure log configuration system properties
OPTS="$OPTS -Dbookkeeper.root.logger=$BOOKIE_ROOT_LOGGER"
OPTS="$OPTS -Dbookkeeper.log.dir=$BOOKIE_LOG_DIR"
OPTS="$OPTS -Dbookkeeper.log.file=$BOOKIE_LOG_FILE"
#Change to BK_HOME to support relative paths
cd "$BK_HOME"
if [ $COMMAND == "bookie" ]; then
exec $JAVA $OPTS $JMX_ARGS org.apache.bookkeeper.proto.BookieServer --conf $BOOKIE_CONF $@
elif [ $COMMAND == "autorecovery" ]; then
exec $JAVA $OPTS $JMX_ARGS org.apache.bookkeeper.replication.AutoRecoveryMain --conf $BOOKIE_CONF $@
elif [ $COMMAND == "localbookie" ]; then
NUMBER=$1
shift
exec $JAVA $OPTS $JMX_ARGS org.apache.bookkeeper.util.LocalBookKeeper $NUMBER $BOOKIE_CONF $@
elif [ $COMMAND == "upgrade" ]; then
exec $JAVA $OPTS org.apache.bookkeeper.bookie.FileSystemUpgrade --conf $BOOKIE_CONF $@
elif [ $COMMAND == "shell" ]; then
ENTRY_FORMATTER_ARG="-DentryFormatterClass=${ENTRY_FORMATTER_CLASS:-org.apache.bookkeeper.util.StringEntryFormatter}"
exec $JAVA $OPTS $ENTRY_FORMATTER_ARG org.apache.bookkeeper.bookie.BookieShell -conf $BOOKIE_CONF $@
elif [ $COMMAND == "help" ]; then
bookkeeper_help;
else
exec $JAVA $OPTS $COMMAND $@
fi
bookkeeper-release-4.2.4/bookkeeper-server/bin/bookkeeper-daemon.sh 0000775 0000000 0000000 00000010545 12445073612 0025426 0 ustar 00root root 0000000 0000000 #!/usr/bin/env bash
#
#/**
# * 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.
# */
usage() {
cat <
where command is one of:
bookie Run the bookie server
where argument is one of:
-force (accepted only with stop command): Decides whether to stop the Bookie Server forcefully if not stopped by normal shutdown
EOF
}
BINDIR=`dirname "$0"`
BK_HOME=`cd $BINDIR/..;pwd`
if [ -f $BK_HOME/conf/bkenv.sh ]
then
. $BK_HOME/conf/bkenv.sh
fi
BOOKIE_LOG_DIR=${BOOKIE_LOG_DIR:-"$BK_HOME/logs"}
BOOKIE_ROOT_LOGGER=${BOOKIE_ROOT_LOGGER:-'INFO,ROLLINGFILE'}
BOOKIE_STOP_TIMEOUT=${BOOKIE_STOP_TIMEOUT:-30}
BOOKIE_PID_DIR=${BOOKIE_PID_DIR:-$BK_HOME/bin}
if [ $# -lt 2 ]
then
echo "Error: no enough arguments provided."
usage
exit 1
fi
startStop=$1
shift
command=$1
shift
case $command in
(bookie)
echo "doing $startStop $command ..."
;;
(autorecovery)
echo "doing $startStop $command ..."
;;
(*)
echo "Error: unknown service name $command"
usage
exit 1
;;
esac
export BOOKIE_LOG_DIR=$BOOKIE_LOG_DIR
export BOOKIE_ROOT_LOGGER=$BOOKIE_ROOT_LOGGER
export BOOKIE_LOG_FILE=bookkeeper-$command-$HOSTNAME.log
pid=$BOOKIE_PID_DIR/bookkeeper-$command.pid
out=$BOOKIE_LOG_DIR/bookkeeper-$command-$HOSTNAME.out
logfile=$BOOKIE_LOG_DIR/$BOOKIE_LOG_FILE
rotate_out_log ()
{
log=$1;
num=5;
if [ -n "$2" ]; then
num=$2
fi
if [ -f "$log" ]; then # rotate logs
while [ $num -gt 1 ]; do
prev=`expr $num - 1`
[ -f "$log.$prev" ] && mv "$log.$prev" "$log.$num"
num=$prev
done
mv "$log" "$log.$num";
fi
}
mkdir -p "$BOOKIE_LOG_DIR"
case $startStop in
(start)
if [ -f $pid ]; then
if kill -0 `cat $pid` > /dev/null 2>&1; then
echo $command running as process `cat $pid`. Stop it first.
exit 1
fi
fi
rotate_out_log $out
echo starting $command, logging to $logfile
bookkeeper=$BK_HOME/bin/bookkeeper
nohup $bookkeeper $command "$@" > "$out" 2>&1 < /dev/null &
echo $! > $pid
sleep 1; head $out
sleep 2;
if ! ps -p $! > /dev/null ; then
exit 1
fi
;;
(stop)
if [ -f $pid ]; then
TARGET_PID=`cat $pid`
if kill -0 $TARGET_PID > /dev/null 2>&1; then
echo stopping $command
kill $TARGET_PID
count=0
location=$BOOKIE_LOG_DIR
while ps -p $TARGET_PID > /dev/null;
do
echo "Shutdown is in progress... Please wait..."
sleep 1
count=`expr $count + 1`
if [ "$count" = "$BOOKIE_STOP_TIMEOUT" ]; then
break
fi
done
if [ "$count" != "$BOOKIE_STOP_TIMEOUT" ]; then
echo "Shutdown completed."
fi
if kill -0 $TARGET_PID > /dev/null 2>&1; then
fileName=$location/$command.out
$JAVA_HOME/bin/jstack $TARGET_PID > $fileName
echo Thread dumps are taken for analysis at $fileName
if [ "$1" == "-force" ]
then
echo forcefully stopping $command
kill -9 $TARGET_PID >/dev/null 2>&1
echo Successfully stopped the process
else
echo "WARNNING : Bookie Server is not stopped completely."
exit 1
fi
fi
else
echo no $command to stop
fi
rm $pid
else
echo no $command to stop
fi
;;
(*)
usage
echo $supportedargs
exit 1
;;
esac
bookkeeper-release-4.2.4/bookkeeper-server/conf/ 0000775 0000000 0000000 00000000000 12445073612 0021650 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/conf/bk_server.conf 0000664 0000000 0000000 00000023151 12445073612 0024503 0 ustar 00root root 0000000 0000000 #!/bin/sh
#
#/**
# * Copyright 2007 The Apache Software Foundation
# *
# * 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.
# */
## Bookie settings
# Port that bookie server listen on
bookiePort=3181
# Set the network interface that the bookie should listen on.
# If not set, the bookie will listen on all interfaces.
#listeningInterface=eth0
# Whether the bookie allowed to use a loopback interface as its primary
# interface(i.e. the interface it uses to establish its identity)?
# By default, loopback interfaces are not allowed as the primary
# interface.
# Using a loopback interface as the primary interface usually indicates
# a configuration error. For example, its fairly common in some VPS setups
# to not configure a hostname, or to have the hostname resolve to
# 127.0.0.1. If this is the case, then all bookies in the cluster will
# establish their identities as 127.0.0.1:3181, and only one will be able
# to join the cluster. For VPSs configured like this, you should explicitly
# set the listening interface.
#allowLoopback=false
# Directory Bookkeeper outputs its write ahead log
journalDirectory=/tmp/bk-txn
# Directory Bookkeeper outputs ledger snapshots
# could define multi directories to store snapshots, separated by ','
# For example:
# ledgerDirectories=/tmp/bk1-data,/tmp/bk2-data
#
# Ideally ledger dirs and journal dir are each in a differet device,
# which reduce the contention between random i/o and sequential write.
# It is possible to run with a single disk, but performance will be significantly lower.
ledgerDirectories=/tmp/bk-data
# Ledger Manager Class
# What kind of ledger manager is used to manage how ledgers are stored, managed
# and garbage collected. Try to read 'BookKeeper Internals' for detail info.
# ledgerManagerType=flat
# Root zookeeper path to store ledger metadata
# This parameter is used by zookeeper-based ledger manager as a root znode to
# store all ledgers.
# zkLedgersRootPath=/ledgers
# Max file size of entry logger, in bytes
# A new entry log file will be created when the old one reaches the file size limitation
# logSizeLimit=2147483648
# Threshold of minor compaction
# For those entry log files whose remaining size percentage reaches below
# this threshold will be compacted in a minor compaction.
# If it is set to less than zero, the minor compaction is disabled.
# minorCompactionThreshold=0.2
# Interval to run minor compaction, in seconds
# If it is set to less than zero, the minor compaction is disabled.
# minorCompactionInterval=3600
# Threshold of major compaction
# For those entry log files whose remaining size percentage reaches below
# this threshold will be compacted in a major compaction.
# Those entry log files whose remaining size percentage is still
# higher than the threshold will never be compacted.
# If it is set to less than zero, the minor compaction is disabled.
# majorCompactionThreshold=0.8
# Interval to run major compaction, in seconds
# If it is set to less than zero, the major compaction is disabled.
# majorCompactionInterval=86400
# Set the maximum number of entries which can be compacted without flushing.
# When compacting, the entries are written to the entrylog and the new offsets
# are cached in memory. Once the entrylog is flushed the index is updated with
# the new offsets. This parameter controls the number of entries added to the
# entrylog before a flush is forced. A higher value for this parameter means
# more memory will be used for offsets. Each offset consists of 3 longs.
# This parameter should _not_ be modified unless you know what you're doing.
# The default is 100,000.
#compactionMaxOutstandingRequests=100000
# Set the rate at which compaction will readd entries. The unit is adds per second.
#compactionRate=1000
# Max file size of journal file, in mega bytes
# A new journal file will be created when the old one reaches the file size limitation
#
# journalMaxSizeMB=2048
# Max number of old journal file to kept
# Keep a number of old journal files would help data recovery in specia case
#
# journalMaxBackups=5
# How long the interval to trigger next garbage collection, in milliseconds
# Since garbage collection is running in background, too frequent gc
# will heart performance. It is better to give a higher number of gc
# interval if there is enough disk capacity.
# gcWaitTime=1000
# How long the interval to flush ledger index pages to disk, in milliseconds
# Flushing index files will introduce much random disk I/O.
# If separating journal dir and ledger dirs each on different devices,
# flushing would not affect performance. But if putting journal dir
# and ledger dirs on same device, performance degrade significantly
# on too frequent flushing. You can consider increment flush interval
# to get better performance, but you need to pay more time on bookie
# server restart after failure.
#
# flushInterval=100
# Interval to watch whether bookie is dead or not, in milliseconds
#
# bookieDeathWatchInterval=1000
## zookeeper client settings
# A list of one of more servers on which zookeeper is running.
# The server list can be comma separated values, for example:
# zkServers=zk1:2181,zk2:2181,zk3:2181
zkServers=localhost:2181
# ZooKeeper client session timeout in milliseconds
# Bookie server will exit if it received SESSION_EXPIRED because it
# was partitioned off from ZooKeeper for more than the session timeout
# JVM garbage collection, disk I/O will cause SESSION_EXPIRED.
# Increment this value could help avoiding this issue
zkTimeout=10000
## NIO Server settings
# This settings is used to enabled/disabled Nagle's algorithm, which is a means of
# improving the efficiency of TCP/IP networks by reducing the number of packets
# that need to be sent over the network.
# If you are sending many small messages, such that more than one can fit in
# a single IP packet, setting server.tcpnodelay to false to enable Nagle algorithm
# can provide better performance.
# Default value is true.
#
# serverTcpNoDelay=true
## ledger cache settings
# Max number of ledger index files could be opened in bookie server
# If number of ledger index files reaches this limitation, bookie
# server started to swap some ledgers from memory to disk.
# Too frequent swap will affect performance. You can tune this number
# to gain performance according your requirements.
# openFileLimit=900
# Size of a index page in ledger cache, in bytes
# A larger index page can improve performance writing page to disk,
# which is efficent when you have small number of ledgers and these
# ledgers have similar number of entries.
# If you have large number of ledgers and each ledger has fewer entries,
# smaller index page would improve memory usage.
# pageSize=8192
# How many index pages provided in ledger cache
# If number of index pages reaches this limitation, bookie server
# starts to swap some ledgers from memory to disk. You can increment
# this value when you found swap became more frequent. But make sure
# pageLimit*pageSize should not more than JVM max memory limitation,
# otherwise you would got OutOfMemoryException.
# In general, incrementing pageLimit, using smaller index page would
# gain bettern performance in lager number of ledgers with fewer entries case
# If pageLimit is -1, bookie server will use 1/3 of JVM memory to compute
# the limitation of number of index pages.
# pageLimit=-1
#If all ledger directories configured are full, then support only read requests for clients.
#If "readOnlyModeEnabled=true" then on all ledger disks full, bookie will be converted
#to read-only mode and serve only read requests. Otherwise the bookie will be shutdown.
#By default this will be disabled.
#readOnlyModeEnabled=false
#For each ledger dir, maximum disk space which can be used.
#Default is 0.95f. i.e. 95% of disk can be used at most after which nothing will
#be written to that partition. If all ledger dir partions are full, then bookie
#will turn to readonly mode if 'readOnlyModeEnabled=true' is set, else it will
#shutdown.
#Valid values should be in between 0 and 1 (exclusive).
#diskUsageThreshold=0.95
#Disk check interval in milli seconds, interval to check the ledger dirs usage.
#Default is 10000
#diskCheckInterval=10000
# Interval at which the auditor will do a check of all ledgers in the cluster.
# By default this runs once a week. The interval is set in seconds.
# To disable the periodic check completely, set this to 0.
# Note that periodic checking will put extra load on the cluster, so it should
# not be run more frequently than once a day.
#auditorPeriodicCheckInterval=604800
# The interval between auditor bookie checks.
# The auditor bookie check, checks ledger metadata to see which bookies should
# contain entries for each ledger. If a bookie which should contain entries is
# unavailable, then the ledger containing that entry is marked for recovery.
# Setting this to 0 disabled the periodic check. Bookie checks will still
# run when a bookie fails.
# The interval is specified in seconds.
#auditorPeriodicBookieCheckInterval=86400
bookkeeper-release-4.2.4/bookkeeper-server/conf/bkenv.sh 0000664 0000000 0000000 00000002714 12445073612 0023315 0 ustar 00root root 0000000 0000000 #!/bin/sh
#
#/**
# * Copyright 2007 The Apache Software Foundation
# *
# * 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.
# */
# Set JAVA_HOME here to override the environment setting
# JAVA_HOME=
# default settings for starting bookkeeper
# Configuration file of settings used in bookie server
# BOOKIE_CONF=
# Log4j configuration file
# BOOKIE_LOG_CONF=
# Logs location
# BOOKIE_LOG_DIR=
# Extra options to be passed to the jvm
# BOOKIE_EXTRA_OPTS=
# Add extra paths to the bookkeeper classpath
# BOOKIE_EXTRA_CLASSPATH=
#Folder where the Bookie server PID file should be stored
#BOOKIE_PID_DIR=
#Wait time before forcefully kill the Bookie server instance, if the stop is not successful
#BOOKIE_STOP_TIMEOUT=
bookkeeper-release-4.2.4/bookkeeper-server/conf/log4j.properties 0000664 0000000 0000000 00000005470 12445073612 0025013 0 ustar 00root root 0000000 0000000 #
#
# 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.
#
#
#
# Hedwig Logging Configuration
#
# Format is " (, )+
# DEFAULT: console appender only
# Define some default values that can be overridden by system properties
bookkeeper.root.logger=WARN,CONSOLE
bookkeeper.log.dir=.
bookkeeper.log.file=bookkeeper-server.log
log4j.rootLogger=${bookkeeper.root.logger}
# Example with rolling log file
#log4j.rootLogger=DEBUG, CONSOLE, ROLLINGFILE
# Example with rolling log file and tracing
#log4j.rootLogger=TRACE, CONSOLE, ROLLINGFILE, TRACEFILE
#
# Log INFO level and above messages to the console
#
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=INFO
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %-5p - [%t:%C{1}@%L] - %m%n
#
# Add ROLLINGFILE to rootLogger to get log file output
# Log DEBUG level and above messages to a log file
log4j.appender.ROLLINGFILE=org.apache.log4j.DailyRollingFileAppender
log4j.appender.ROLLINGFILE.Threshold=INFO
log4j.appender.ROLLINGFILE.File=${bookkeeper.log.dir}/${bookkeeper.log.file}
log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p - [%t:%C{1}@%L] - %m%n
# Max log file size of 10MB
#log4j.appender.ROLLINGFILE.MaxFileSize=10MB
# uncomment the next line to limit number of backup files
#log4j.appender.ROLLINGFILE.MaxBackupIndex=10
log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n
#
# Add TRACEFILE to rootLogger to get log file output
# Log DEBUG level and above messages to a log file
log4j.appender.TRACEFILE=org.apache.log4j.FileAppender
log4j.appender.TRACEFILE.Threshold=TRACE
log4j.appender.TRACEFILE.File=bookkeeper-trace.log
log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout
### Notice we are including log4j's NDC here (%x)
log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L][%x] - %m%n
bookkeeper-release-4.2.4/bookkeeper-server/conf/log4j.shell.properties 0000664 0000000 0000000 00000002633 12445073612 0026117 0 ustar 00root root 0000000 0000000 #
#
# 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.
#
#
#
# BookieShell configuration
# DEFAULT: console appender only
# Define some default values that can be overridden by system properties
bookkeeper.root.logger=ERROR,CONSOLE
log4j.rootLogger=${bookkeeper.root.logger}
#
# Log INFO level and above messages to the console
#
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=INFO
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ABSOLUTE} %-5p %m%n
log4j.logger.org.apache.zookeeper=ERROR
log4j.logger.org.apache.bookkeeper=ERROR
log4j.logger.org.apache.bookkeeper.bookie.BookieShell=INFO
bookkeeper-release-4.2.4/bookkeeper-server/pom.xml 0000664 0000000 0000000 00000024020 12445073612 0022236 0 ustar 00root root 0000000 0000000
4.0.0
bookkeeper
org.apache.bookkeeper
4.2.4
org.apache.bookkeeper
bookkeeper-server
bookkeeper-server
http://maven.apache.org
UTF-8
${basedir}/lib
com.google.protobuf
protobuf-java
${protobuf.version}
compile
com.google.guava
guava
${guava.version}
junit
junit
4.8.1
test
org.slf4j
slf4j-api
1.6.4
org.slf4j
slf4j-log4j12
1.6.4
org.apache.zookeeper
zookeeper
3.4.3
compile
org.apache.zookeeper
zookeeper
3.4.3
test-jar
test
org.jboss.netty
netty
3.2.4.Final
compile
commons-configuration
commons-configuration
1.6
commons-cli
commons-cli
1.2
commons-codec
commons-codec
1.6
commons-io
commons-io
2.1
log4j
log4j
1.2.15
javax.mail
mail
javax.jms
jms
com.sun.jdmk
jmxtools
com.sun.jmx
jmxri
org.apache.bookkeeper
bookkeeper-server-compat400
4.0.0
test
org.apache.bookkeeper
bookkeeper-server
org.apache.bookkeeper
bookkeeper-server-compat410
4.1.0
test
org.apache.bookkeeper
bookkeeper-server
org.apache.maven.plugins
maven-shade-plugin
2.1
package
shade
true
com.google.protobuf:protobuf-java
com.google.guava:guava
true
com.google
bk-shade.com.google
org.codehaus.mojo
license-maven-plugin
1.6
false
${project.basedir}
update-pom-license
update-file-header
package
apache_v2
dependency-reduced-pom.xml
org.apache.maven.plugins
maven-jar-plugin
2.2
test-jar
maven-assembly-plugin
2.2.1
../src/assemble/bin.xml
org.apache.rat
apache-rat-plugin
0.7
**/DataFormats.java
org.codehaus.mojo
findbugs-maven-plugin
${basedir}/src/main/resources/findbugsExclude.xml
maven-dependency-plugin
package
copy-dependencies
${project.libdir}
runtime
maven-clean-plugin
2.5
${project.libdir}
false
${project.basedir}
dependency-reduced-pom.xml
protobuf
maven-antrun-plugin
generate-sources
default-cli
run
bookkeeper-release-4.2.4/bookkeeper-server/src/ 0000775 0000000 0000000 00000000000 12445073612 0021512 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/ 0000775 0000000 0000000 00000000000 12445073612 0022436 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/ 0000775 0000000 0000000 00000000000 12445073612 0023357 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/ 0000775 0000000 0000000 00000000000 12445073612 0024146 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/ 0000775 0000000 0000000 00000000000 12445073612 0025367 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/ 0000775 0000000 0000000 00000000000 12445073612 0027515 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/ 0000775 0000000 0000000 00000000000 12445073612 0030765 5 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/Bookie.java 0000664 0000000 0000000 00000134464 12445073612 0033054 0 ustar 00root root 0000000 0000000 /**
*
* 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.bookie;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.FilenameFilter;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.bookkeeper.meta.LedgerManager;
import org.apache.bookkeeper.meta.LedgerManagerFactory;
import org.apache.bookkeeper.bookie.BookieException;
import org.apache.bookkeeper.bookie.Journal.JournalScanner;
import org.apache.bookkeeper.bookie.LedgerDirsManager.LedgerDirsListener;
import org.apache.bookkeeper.bookie.LedgerDirsManager.NoWritableLedgerDirException;
import org.apache.bookkeeper.conf.ServerConfiguration;
import org.apache.bookkeeper.jmx.BKMBeanInfo;
import org.apache.bookkeeper.jmx.BKMBeanRegistry;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.WriteCallback;
import org.apache.bookkeeper.util.BookKeeperConstants;
import org.apache.bookkeeper.util.IOUtils;
import org.apache.bookkeeper.util.MathUtils;
import org.apache.bookkeeper.util.ZkUtils;
import org.apache.bookkeeper.util.StringUtils;
import org.apache.bookkeeper.net.DNS;
import org.apache.bookkeeper.zookeeper.ZooKeeperWatcherBase;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException.NodeExistsException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.Watcher.Event.EventType;
import com.google.common.annotations.VisibleForTesting;
/**
* Implements a bookie.
*
*/
public class Bookie extends Thread {
static Logger LOG = LoggerFactory.getLogger(Bookie.class);
final File journalDirectory;
final ServerConfiguration conf;
final SyncThread syncThread;
final LedgerManagerFactory ledgerManagerFactory;
final LedgerManager ledgerManager;
final LedgerStorage ledgerStorage;
final Journal journal;
final HandleFactory handles;
static final long METAENTRY_ID_LEDGER_KEY = -0x1000;
static final long METAENTRY_ID_FENCE_KEY = -0x2000;
// ZK registration path for this bookie
private final String bookieRegistrationPath;
private LedgerDirsManager ledgerDirsManager;
// ZooKeeper client instance for the Bookie
ZooKeeper zk;
// Running flag
private volatile boolean running = false;
// Flag identify whether it is in shutting down progress
private volatile boolean shuttingdown = false;
private int exitCode = ExitCode.OK;
// jmx related beans
BookieBean jmxBookieBean;
BKMBeanInfo jmxLedgerStorageBean;
Map masterKeyCache = Collections.synchronizedMap(new HashMap());
final private String zkBookieRegPath;
final private AtomicBoolean readOnly = new AtomicBoolean(false);
public static class NoLedgerException extends IOException {
private static final long serialVersionUID = 1L;
private long ledgerId;
public NoLedgerException(long ledgerId) {
super("Ledger " + ledgerId + " not found");
this.ledgerId = ledgerId;
}
public long getLedgerId() {
return ledgerId;
}
}
public static class NoEntryException extends IOException {
private static final long serialVersionUID = 1L;
private long ledgerId;
private long entryId;
public NoEntryException(long ledgerId, long entryId) {
this("Entry " + entryId + " not found in " + ledgerId, ledgerId, entryId);
}
public NoEntryException(String msg, long ledgerId, long entryId) {
super(msg);
this.ledgerId = ledgerId;
this.entryId = entryId;
}
public long getLedger() {
return ledgerId;
}
public long getEntry() {
return entryId;
}
}
// Write Callback do nothing
static class NopWriteCallback implements WriteCallback {
@Override
public void writeComplete(int rc, long ledgerId, long entryId,
InetSocketAddress addr, Object ctx) {
if (LOG.isDebugEnabled()) {
LOG.debug("Finished writing entry {} @ ledger {} for {} : {}",
new Object[] { entryId, ledgerId, addr, rc });
}
}
}
final static Future SUCCESS_FUTURE = new Future() {
@Override
public boolean cancel(boolean mayInterruptIfRunning) { return false; }
@Override
public Boolean get() { return true; }
@Override
public Boolean get(long timeout, TimeUnit unit) { return true; }
@Override
public boolean isCancelled() { return false; }
@Override
public boolean isDone() {
return true;
}
};
static class CountDownLatchFuture implements Future {
T value = null;
volatile boolean done = false;
CountDownLatch latch = new CountDownLatch(1);
@Override
public boolean cancel(boolean mayInterruptIfRunning) { return false; }
@Override
public T get() throws InterruptedException {
latch.await();
return value;
}
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException {
latch.await(timeout, unit);
return value;
}
@Override
public boolean isCancelled() { return false; }
@Override
public boolean isDone() {
return done;
}
void setDone(T value) {
this.value = value;
done = true;
latch.countDown();
}
}
static class FutureWriteCallback implements WriteCallback {
CountDownLatchFuture result =
new CountDownLatchFuture();
@Override
public void writeComplete(int rc, long ledgerId, long entryId,
InetSocketAddress addr, Object ctx) {
if (LOG.isDebugEnabled()) {
LOG.debug("Finished writing entry {} @ ledger {} for {} : {}",
new Object[] { entryId, ledgerId, addr, rc });
}
result.setDone(0 == rc);
}
public Future getResult() {
return result;
}
}
/**
* SyncThread is a background thread which flushes ledger index pages periodically.
* Also it takes responsibility of garbage collecting journal files.
*
*
* Before flushing, SyncThread first records a log marker {journalId, journalPos} in memory,
* which indicates entries before this log marker would be persisted to ledger files.
* Then sync thread begins flushing ledger index pages to ledger index files, flush entry
* logger to ensure all entries persisted to entry loggers for future reads.
*
*
* After all data has been persisted to ledger index files and entry loggers, it is safe
* to persist the log marker to disk. If bookie failed after persist log mark,
* bookie is able to relay journal entries started from last log mark without losing
* any entries.
*
*
* Those journal files whose id are less than the log id in last log mark, could be
* removed safely after persisting last log mark. We provide a setting to let user keeping
* number of old journal files which may be used for manual recovery in critical disaster.
*
*/
class SyncThread extends Thread {
volatile boolean running = true;
// flag to ensure sync thread will not be interrupted during flush
final AtomicBoolean flushing = new AtomicBoolean(false);
// make flush interval as a parameter
final int flushInterval;
public SyncThread(ServerConfiguration conf) {
super("SyncThread");
flushInterval = conf.getFlushInterval();
LOG.debug("Flush Interval : {}", flushInterval);
}
private Object suspensionLock = new Object();
private boolean suspended = false;
/**
* Suspend sync thread. (for testing)
*/
@VisibleForTesting
public void suspendSync() {
synchronized(suspensionLock) {
suspended = true;
}
}
/**
* Resume sync thread. (for testing)
*/
@VisibleForTesting
public void resumeSync() {
synchronized(suspensionLock) {
suspended = false;
suspensionLock.notify();
}
}
@Override
public void run() {
try {
while (running) {
synchronized (this) {
try {
wait(flushInterval);
if (!ledgerStorage.isFlushRequired()) {
continue;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
continue;
}
}
synchronized (suspensionLock) {
while (suspended) {
suspensionLock.wait();
}
}
// try to mark flushing flag to make sure it would not be interrupted
// by shutdown during flushing. otherwise it will receive
// ClosedByInterruptException which may cause index file & entry logger
// closed and corrupted.
if (!flushing.compareAndSet(false, true)) {
// set flushing flag failed, means flushing is true now
// indicates another thread wants to interrupt sync thread to exit
break;
}
// journal mark log
journal.markLog();
boolean flushFailed = false;
try {
ledgerStorage.flush();
} catch (NoWritableLedgerDirException e) {
flushFailed = true;
flushing.set(false);
transitionToReadOnlyMode();
} catch (IOException e) {
LOG.error("Exception flushing Ledger", e);
flushFailed = true;
}
// if flush failed, we should not roll last mark, otherwise we would
// have some ledgers are not flushed and their journal entries were lost
if (!flushFailed) {
try {
journal.rollLog();
journal.gcJournals();
} catch (NoWritableLedgerDirException e) {
flushing.set(false);
transitionToReadOnlyMode();
}
}
// clear flushing flag
flushing.set(false);
}
} catch (Throwable t) {
LOG.error("Exception in SyncThread", t);
flushing.set(false);
triggerBookieShutdown(ExitCode.BOOKIE_EXCEPTION);
}
}
// shutdown sync thread
void shutdown() throws InterruptedException {
running = false;
if (flushing.compareAndSet(false, true)) {
// if setting flushing flag succeed, means syncThread is not flushing now
// it is safe to interrupt itself now
this.interrupt();
}
this.join();
}
}
public static void checkDirectoryStructure(File dir) throws IOException {
if (!dir.exists()) {
File parent = dir.getParentFile();
File preV3versionFile = new File(dir.getParent(),
BookKeeperConstants.VERSION_FILENAME);
final AtomicBoolean oldDataExists = new AtomicBoolean(false);
parent.list(new FilenameFilter() {
public boolean accept(File dir, String name) {
if (name.endsWith(".txn") || name.endsWith(".idx") || name.endsWith(".log")) {
oldDataExists.set(true);
}
return true;
}
});
if (preV3versionFile.exists() || oldDataExists.get()) {
String err = "Directory layout version is less than 3, upgrade needed";
LOG.error(err);
throw new IOException(err);
}
if (!dir.mkdirs()) {
String err = "Unable to create directory " + dir;
LOG.error(err);
throw new IOException(err);
}
}
}
/**
* Check that the environment for the bookie is correct.
* This means that the configuration has stayed the same as the
* first run and the filesystem structure is up to date.
*/
private void checkEnvironment(ZooKeeper zk) throws BookieException, IOException {
if (zk == null) { // exists only for testing, just make sure directories are correct
checkDirectoryStructure(journalDirectory);
for (File dir : ledgerDirsManager.getAllLedgerDirs()) {
checkDirectoryStructure(dir);
}
return;
}
try {
String instanceId = getInstanceId(zk);
boolean newEnv = false;
Cookie masterCookie = Cookie.generateCookie(conf);
if (null != instanceId) {
masterCookie.setInstanceId(instanceId);
}
try {
Cookie zkCookie = Cookie.readFromZooKeeper(zk, conf);
masterCookie.verify(zkCookie);
} catch (KeeperException.NoNodeException nne) {
newEnv = true;
}
List missedCookieDirs = new ArrayList();
checkDirectoryStructure(journalDirectory);
// try to read cookie from journal directory
try {
Cookie journalCookie = Cookie.readFromDirectory(journalDirectory);
journalCookie.verify(masterCookie);
} catch (FileNotFoundException fnf) {
missedCookieDirs.add(journalDirectory);
}
for (File dir : ledgerDirsManager.getAllLedgerDirs()) {
checkDirectoryStructure(dir);
try {
Cookie c = Cookie.readFromDirectory(dir);
c.verify(masterCookie);
} catch (FileNotFoundException fnf) {
missedCookieDirs.add(dir);
}
}
if (!newEnv && missedCookieDirs.size() > 0){
LOG.error("Cookie exists in zookeeper, but not in all local directories. "
+ " Directories missing cookie file are " + missedCookieDirs);
throw new BookieException.InvalidCookieException();
}
if (newEnv) {
if (missedCookieDirs.size() > 0) {
LOG.debug("Directories missing cookie file are {}", missedCookieDirs);
masterCookie.writeToDirectory(journalDirectory);
for (File dir : ledgerDirsManager.getAllLedgerDirs()) {
masterCookie.writeToDirectory(dir);
}
}
masterCookie.writeToZooKeeper(zk, conf);
}
} catch (KeeperException ke) {
LOG.error("Couldn't access cookie in zookeeper", ke);
throw new BookieException.InvalidCookieException(ke);
} catch (UnknownHostException uhe) {
LOG.error("Couldn't check cookies, networking is broken", uhe);
throw new BookieException.InvalidCookieException(uhe);
} catch (IOException ioe) {
LOG.error("Error accessing cookie on disks", ioe);
throw new BookieException.InvalidCookieException(ioe);
} catch (InterruptedException ie) {
LOG.error("Thread interrupted while checking cookies, exiting", ie);
throw new BookieException.InvalidCookieException(ie);
}
}
/**
* Return the configured address of the bookie.
*/
public static InetSocketAddress getBookieAddress(ServerConfiguration conf)
throws UnknownHostException {
String iface = conf.getListeningInterface();
if (iface == null) {
iface = "default";
}
InetSocketAddress addr = new InetSocketAddress(
DNS.getDefaultHost(iface),
conf.getBookiePort());
if (addr.getAddress().isLoopbackAddress()
&& !conf.getAllowLoopback()) {
throw new UnknownHostException("Trying to listen on loopback address, "
+ addr + " but this is forbidden by default "
+ "(see ServerConfiguration#getAllowLoopback())");
}
return addr;
}
private String getInstanceId(ZooKeeper zk) throws KeeperException,
InterruptedException {
String instanceId = null;
if (zk.exists(conf.getZkLedgersRootPath(), null) == null) {
LOG.error("BookKeeper metadata doesn't exist in zookeeper. "
+ "Has the cluster been initialized? "
+ "Try running bin/bookkeeper shell metaformat");
throw new KeeperException.NoNodeException("BookKeeper metadata");
}
try {
byte[] data = zk.getData(conf.getZkLedgersRootPath() + "/"
+ BookKeeperConstants.INSTANCEID, false, null);
instanceId = new String(data);
} catch (KeeperException.NoNodeException e) {
LOG.info("INSTANCEID not exists in zookeeper. Not considering it for data verification");
}
return instanceId;
}
public LedgerDirsManager getLedgerDirsManager() {
return ledgerDirsManager;
}
public static File getCurrentDirectory(File dir) {
return new File(dir, BookKeeperConstants.CURRENT_DIR);
}
public static File[] getCurrentDirectories(File[] dirs) {
File[] currentDirs = new File[dirs.length];
for (int i = 0; i < dirs.length; i++) {
currentDirs[i] = getCurrentDirectory(dirs[i]);
}
return currentDirs;
}
public Bookie(ServerConfiguration conf)
throws IOException, KeeperException, InterruptedException, BookieException {
super("Bookie-" + conf.getBookiePort());
this.bookieRegistrationPath = conf.getZkAvailableBookiesPath() + "/";
this.conf = conf;
this.journalDirectory = getCurrentDirectory(conf.getJournalDir());
this.ledgerDirsManager = new LedgerDirsManager(conf);
// instantiate zookeeper client to initialize ledger manager
this.zk = instantiateZookeeperClient(conf);
checkEnvironment(this.zk);
ledgerManagerFactory = LedgerManagerFactory.newLedgerManagerFactory(conf, this.zk);
LOG.info("instantiate ledger manager {}", ledgerManagerFactory.getClass().getName());
ledgerManager = ledgerManagerFactory.newLedgerManager();
syncThread = new SyncThread(conf);
ledgerStorage = new InterleavedLedgerStorage(conf, ledgerManager,
ledgerDirsManager);
handles = new HandleFactoryImpl(ledgerStorage);
// instantiate the journal
journal = new Journal(conf, ledgerDirsManager);
// ZK ephemeral node for this Bookie.
zkBookieRegPath = this.bookieRegistrationPath + getMyId();
}
private String getMyId() throws UnknownHostException {
return StringUtils.addrToString(Bookie.getBookieAddress(conf));
}
void readJournal() throws IOException, BookieException {
journal.replay(new JournalScanner() {
@Override
public void process(int journalVersion, long offset, ByteBuffer recBuff) throws IOException {
long ledgerId = recBuff.getLong();
long entryId = recBuff.getLong();
try {
LOG.debug("Replay journal - ledger id : {}, entry id : {}.", ledgerId, entryId);
if (entryId == METAENTRY_ID_LEDGER_KEY) {
if (journalVersion >= 3) {
int masterKeyLen = recBuff.getInt();
byte[] masterKey = new byte[masterKeyLen];
recBuff.get(masterKey);
masterKeyCache.put(ledgerId, masterKey);
} else {
throw new IOException("Invalid journal. Contains journalKey "
+ " but layout version (" + journalVersion
+ ") is too old to hold this");
}
} else if (entryId == METAENTRY_ID_FENCE_KEY) {
if (journalVersion >= 4) {
byte[] key = masterKeyCache.get(ledgerId);
if (key == null) {
key = ledgerStorage.readMasterKey(ledgerId);
}
LedgerDescriptor handle = handles.getHandle(ledgerId, key);
handle.setFenced();
} else {
throw new IOException("Invalid journal. Contains fenceKey "
+ " but layout version (" + journalVersion
+ ") is too old to hold this");
}
} else {
byte[] key = masterKeyCache.get(ledgerId);
if (key == null) {
key = ledgerStorage.readMasterKey(ledgerId);
}
LedgerDescriptor handle = handles.getHandle(ledgerId, key);
recBuff.rewind();
handle.addEntry(recBuff);
}
} catch (NoLedgerException nsle) {
LOG.debug("Skip replaying entries of ledger {} since it was deleted.", ledgerId);
} catch (BookieException be) {
throw new IOException(be);
}
}
});
}
synchronized public void start() {
setDaemon(true);
LOG.debug("I'm starting a bookie with journal directory {}", journalDirectory.getName());
// replay journals
try {
readJournal();
} catch (IOException ioe) {
LOG.error("Exception while replaying journals, shutting down", ioe);
shutdown(ExitCode.BOOKIE_EXCEPTION);
return;
} catch (BookieException be) {
LOG.error("Exception while replaying journals, shutting down", be);
shutdown(ExitCode.BOOKIE_EXCEPTION);
return;
}
LOG.info("Finished reading journal, starting bookie");
// start bookie thread
super.start();
ledgerDirsManager.addLedgerDirsListener(getLedgerDirsListener());
//Start DiskChecker thread
ledgerDirsManager.start();
ledgerStorage.start();
syncThread.start();
// set running here.
// since bookie server use running as a flag to tell bookie server whether it is alive
// if setting it in bookie thread, the watcher might run before bookie thread.
running = true;
try {
registerBookie(conf);
} catch (IOException e) {
LOG.error("Couldn't register bookie with zookeeper, shutting down", e);
shutdown(ExitCode.ZK_REG_FAIL);
}
}
/*
* Get the DiskFailure listener for the bookie
*/
private LedgerDirsListener getLedgerDirsListener() {
return new LedgerDirsListener() {
@Override
public void diskFull(File disk) {
// Nothing needs to be handled here.
}
@Override
public void diskFailed(File disk) {
// Shutdown the bookie on disk failure.
triggerBookieShutdown(ExitCode.BOOKIE_EXCEPTION);
}
@Override
public void allDisksFull() {
// Transition to readOnly mode on all disks full
transitionToReadOnlyMode();
}
@Override
public void fatalError() {
LOG.error("Fatal error reported by ledgerDirsManager");
triggerBookieShutdown(ExitCode.BOOKIE_EXCEPTION);
}
};
}
/**
* Register jmx with parent
*
* @param parent parent bk mbean info
*/
public void registerJMX(BKMBeanInfo parent) {
try {
jmxBookieBean = new BookieBean(this);
BKMBeanRegistry.getInstance().register(jmxBookieBean, parent);
try {
jmxLedgerStorageBean = this.ledgerStorage.getJMXBean();
BKMBeanRegistry.getInstance().register(jmxLedgerStorageBean, jmxBookieBean);
} catch (Exception e) {
LOG.warn("Failed to register with JMX for ledger cache", e);
jmxLedgerStorageBean = null;
}
} catch (Exception e) {
LOG.warn("Failed to register with JMX", e);
jmxBookieBean = null;
}
}
/**
* Unregister jmx
*/
public void unregisterJMX() {
try {
if (jmxLedgerStorageBean != null) {
BKMBeanRegistry.getInstance().unregister(jmxLedgerStorageBean);
}
} catch (Exception e) {
LOG.warn("Failed to unregister with JMX", e);
}
try {
if (jmxBookieBean != null) {
BKMBeanRegistry.getInstance().unregister(jmxBookieBean);
}
} catch (Exception e) {
LOG.warn("Failed to unregister with JMX", e);
}
jmxBookieBean = null;
jmxLedgerStorageBean = null;
}
/**
* Instantiate the ZooKeeper client for the Bookie.
*/
private ZooKeeper instantiateZookeeperClient(ServerConfiguration conf)
throws IOException, InterruptedException, KeeperException {
if (conf.getZkServers() == null) {
LOG.warn("No ZK servers passed to Bookie constructor so BookKeeper clients won't know about this server!");
return null;
}
// Create the ZooKeeper client instance
return newZookeeper(conf.getZkServers(), conf.getZkTimeout());
}
/**
* Register as an available bookie
*/
protected void registerBookie(ServerConfiguration conf) throws IOException {
if (null == zk) {
// zookeeper instance is null, means not register itself to zk
return;
}
// ZK ephemeral node for this Bookie.
String zkBookieRegPath = this.bookieRegistrationPath
+ StringUtils.addrToString(getBookieAddress(conf));
final CountDownLatch prevNodeLatch = new CountDownLatch(1);
try{
Watcher zkPrevRegNodewatcher = new Watcher() {
@Override
public void process(WatchedEvent event) {
// Check for prev znode deletion. Connection expiration is
// not handling, since bookie has logic to shutdown.
if (EventType.NodeDeleted == event.getType()) {
prevNodeLatch.countDown();
}
}
};
if (null != zk.exists(zkBookieRegPath, zkPrevRegNodewatcher)) {
LOG.info("Previous bookie registration znode: "
+ zkBookieRegPath
+ " exists, so waiting zk sessiontimeout: "
+ conf.getZkTimeout() + "ms for znode deletion");
// waiting for the previous bookie reg znode deletion
if (!prevNodeLatch.await(conf.getZkTimeout(),
TimeUnit.MILLISECONDS)) {
throw new KeeperException.NodeExistsException(
zkBookieRegPath);
}
}
// Create the ZK ephemeral node for this Bookie.
zk.create(zkBookieRegPath, new byte[0], Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL);
} catch (KeeperException ke) {
LOG.error("ZK exception registering ephemeral Znode for Bookie!",
ke);
// Throw an IOException back up. This will cause the Bookie
// constructor to error out. Alternatively, we could do a System
// exit here as this is a fatal error.
throw new IOException(ke);
} catch (InterruptedException ie) {
LOG.error("ZK exception registering ephemeral Znode for Bookie!",
ie);
// Throw an IOException back up. This will cause the Bookie
// constructor to error out. Alternatively, we could do a System
// exit here as this is a fatal error.
throw new IOException(ie);
}
}
/*
* Transition the bookie to readOnly mode
*/
@VisibleForTesting
public void transitionToReadOnlyMode() {
if (shuttingdown == true) {
return;
}
if (!readOnly.compareAndSet(false, true)) {
return;
}
if (!conf.isReadOnlyModeEnabled()) {
LOG.warn("ReadOnly mode is not enabled. "
+ "Can be enabled by configuring "
+ "'readOnlyModeEnabled=true' in configuration."
+ "Shutting down bookie");
triggerBookieShutdown(ExitCode.BOOKIE_EXCEPTION);
return;
}
LOG.info("Transitioning Bookie to ReadOnly mode,"
+ " and will serve only read requests from clients!");
try {
if (null == zk.exists(this.bookieRegistrationPath
+ BookKeeperConstants.READONLY, false)) {
try {
zk.create(this.bookieRegistrationPath
+ BookKeeperConstants.READONLY, new byte[0],
Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
} catch (NodeExistsException e) {
// this node is just now created by someone.
}
}
// Create the readonly node
zk.create(this.bookieRegistrationPath
+ BookKeeperConstants.READONLY + "/" + getMyId(),
new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
// Clear the current registered node
zk.delete(zkBookieRegPath, -1);
} catch (IOException e) {
LOG.error("Error in transition to ReadOnly Mode."
+ " Shutting down", e);
triggerBookieShutdown(ExitCode.BOOKIE_EXCEPTION);
return;
} catch (KeeperException e) {
LOG.error("Error in transition to ReadOnly Mode."
+ " Shutting down", e);
triggerBookieShutdown(ExitCode.BOOKIE_EXCEPTION);
return;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.warn("Interrupted Exception while transitioning to ReadOnly Mode.");
return;
}
}
/*
* Check whether Bookie is writable
*/
public boolean isReadOnly() {
return readOnly.get();
}
/**
* Create a new zookeeper client to zk cluster.
*
*
* Bookie Server just used zk client when syncing ledgers for garbage collection.
* So when zk client is expired, it means this bookie server is not available in
* bookie server list. The bookie client will be notified for its expiration. No
* more bookie request will be sent to this server. So it's better to exit when zk
* expired.
*
*
* Since there are lots of bk operations cached in queue, so we wait for all the operations
* are processed and quit. It is done by calling shutdown.
*
*
* @param zkServers the quorum list of zk servers
* @param sessionTimeout session timeout of zk connection
*
* @return zk client instance
*/
private ZooKeeper newZookeeper(final String zkServers,
final int sessionTimeout) throws IOException, InterruptedException,
KeeperException {
ZooKeeperWatcherBase w = new ZooKeeperWatcherBase(conf.getZkTimeout()) {
@Override
public void process(WatchedEvent event) {
// Check for expired connection.
if (event.getState().equals(Watcher.Event.KeeperState.Expired)) {
LOG.error("ZK client connection to the ZK server has expired!");
shutdown(ExitCode.ZK_EXPIRED);
} else {
super.process(event);
}
}
};
return ZkUtils.createConnectedZookeeperClient(zkServers, w);
}
public boolean isRunning() {
return running;
}
@Override
public void run() {
// bookie thread wait for journal thread
try {
// start journal
journal.start();
// wait until journal quits
journal.join();
} catch (InterruptedException ie) {
}
// if the journal thread quits due to shutting down, it is ok
if (!shuttingdown) {
// some error found in journal thread and it quits
// following add operations to it would hang unit client timeout
// so we should let bookie server exists
LOG.error("Journal manager quits unexpectedly.");
triggerBookieShutdown(ExitCode.BOOKIE_EXCEPTION);
}
}
// Triggering the Bookie shutdown in its own thread,
// because shutdown can be called from sync thread which would be
// interrupted by shutdown call.
AtomicBoolean shutdownTriggered = new AtomicBoolean(false);
void triggerBookieShutdown(final int exitCode) {
if (!shutdownTriggered.compareAndSet(false, true)) {
return;
}
LOG.info("Triggering shutdown of Bookie-{} with exitCode {}",
conf.getBookiePort(), exitCode);
new Thread("BookieShutdownTrigger") {
public void run() {
Bookie.this.shutdown(exitCode);
}
}.start();
}
// provided a public shutdown method for other caller
// to shut down bookie gracefully
public int shutdown() {
return shutdown(ExitCode.OK);
}
// internal shutdown method to let shutdown bookie gracefully
// when encountering exception
synchronized int shutdown(int exitCode) {
try {
if (running) { // avoid shutdown twice
// the exitCode only set when first shutdown usually due to exception found
LOG.info("Shutting down Bookie-{} with exitCode {}",
conf.getBookiePort(), exitCode);
this.exitCode = exitCode;
// mark bookie as in shutting down progress
shuttingdown = true;
// Shutdown journal
journal.shutdown();
this.join();
syncThread.shutdown();
// Shutdown the EntryLogger which has the GarbageCollector Thread running
ledgerStorage.shutdown();
// close Ledger Manager
try {
ledgerManager.close();
ledgerManagerFactory.uninitialize();
} catch (IOException ie) {
LOG.error("Failed to close active ledger manager : ", ie);
}
//Shutdown disk checker
ledgerDirsManager.shutdown();
// Shutdown the ZK client
if(zk != null) zk.close();
// setting running to false here, so watch thread
// in bookie server know it only after bookie shut down
running = false;
}
} catch (InterruptedException ie) {
LOG.error("Interrupted during shutting down bookie : ", ie);
}
return this.exitCode;
}
/**
* Retrieve the ledger descriptor for the ledger which entry should be added to.
* The LedgerDescriptor returned from this method should be eventually freed with
* #putHandle().
*
* @throws BookieException if masterKey does not match the master key of the ledger
*/
private LedgerDescriptor getLedgerForEntry(ByteBuffer entry, byte[] masterKey)
throws IOException, BookieException {
long ledgerId = entry.getLong();
LedgerDescriptor l = handles.getHandle(ledgerId, masterKey);
if (!masterKeyCache.containsKey(ledgerId)) {
// new handle, we should add the key to journal ensure we can rebuild
ByteBuffer bb = ByteBuffer.allocate(8 + 8 + 4 + masterKey.length);
bb.putLong(ledgerId);
bb.putLong(METAENTRY_ID_LEDGER_KEY);
bb.putInt(masterKey.length);
bb.put(masterKey);
bb.flip();
journal.logAddEntry(bb, new NopWriteCallback(), null);
masterKeyCache.put(ledgerId, masterKey);
}
return l;
}
protected void addEntryByLedgerId(long ledgerId, ByteBuffer entry)
throws IOException, BookieException {
byte[] key = ledgerStorage.readMasterKey(ledgerId);
LedgerDescriptor handle = handles.getHandle(ledgerId, key);
handle.addEntry(entry);
}
/**
* Add an entry to a ledger as specified by handle.
*/
private void addEntryInternal(LedgerDescriptor handle, ByteBuffer entry, WriteCallback cb, Object ctx)
throws IOException, BookieException {
long ledgerId = handle.getLedgerId();
entry.rewind();
long entryId = handle.addEntry(entry);
entry.rewind();
LOG.trace("Adding {}@{}", entryId, ledgerId);
journal.logAddEntry(entry, cb, ctx);
}
/**
* Add entry to a ledger, even if the ledger has previous been fenced. This should only
* happen in bookie recovery or ledger recovery cases, where entries are being replicates
* so that they exist on a quorum of bookies. The corresponding client side call for this
* is not exposed to users.
*/
public void recoveryAddEntry(ByteBuffer entry, WriteCallback cb, Object ctx, byte[] masterKey)
throws IOException, BookieException {
try {
LedgerDescriptor handle = getLedgerForEntry(entry, masterKey);
synchronized (handle) {
addEntryInternal(handle, entry, cb, ctx);
}
} catch (NoWritableLedgerDirException e) {
transitionToReadOnlyMode();
throw new IOException(e);
}
}
/**
* Add entry to a ledger.
* @throws BookieException.LedgerFencedException if the ledger is fenced
*/
public void addEntry(ByteBuffer entry, WriteCallback cb, Object ctx, byte[] masterKey)
throws IOException, BookieException {
try {
LedgerDescriptor handle = getLedgerForEntry(entry, masterKey);
synchronized (handle) {
if (handle.isFenced()) {
throw BookieException
.create(BookieException.Code.LedgerFencedException);
}
addEntryInternal(handle, entry, cb, ctx);
}
} catch (NoWritableLedgerDirException e) {
transitionToReadOnlyMode();
throw new IOException(e);
}
}
/**
* Fences a ledger. From this point on, clients will be unable to
* write to this ledger. Only recoveryAddEntry will be
* able to add entries to the ledger.
* This method is idempotent. Once a ledger is fenced, it can
* never be unfenced. Fencing a fenced ledger has no effect.
*/
public Future fenceLedger(long ledgerId, byte[] masterKey) throws IOException, BookieException {
LedgerDescriptor handle = handles.getHandle(ledgerId, masterKey);
boolean success;
synchronized (handle) {
success = handle.setFenced();
}
if (success) {
// fenced first time, we should add the key to journal ensure we can rebuild
ByteBuffer bb = ByteBuffer.allocate(8 + 8);
bb.putLong(ledgerId);
bb.putLong(METAENTRY_ID_FENCE_KEY);
bb.flip();
FutureWriteCallback fwc = new FutureWriteCallback();
LOG.debug("record fenced state for ledger {} in journal.", ledgerId);
journal.logAddEntry(bb, fwc, null);
return fwc.getResult();
} else {
// already fenced
return SUCCESS_FUTURE;
}
}
public ByteBuffer readEntry(long ledgerId, long entryId)
throws IOException, NoLedgerException {
LedgerDescriptor handle = handles.getReadOnlyHandle(ledgerId);
LOG.trace("Reading {}@{}", entryId, ledgerId);
return handle.readEntry(entryId);
}
// The rest of the code is test stuff
static class CounterCallback implements WriteCallback {
int count;
synchronized public void writeComplete(int rc, long l, long e, InetSocketAddress addr, Object ctx) {
count--;
if (count == 0) {
notifyAll();
}
}
synchronized public void incCount() {
count++;
}
synchronized public void waitZero() throws InterruptedException {
while (count > 0) {
wait();
}
}
}
/**
* Format the bookie server data
*
* @param conf
* ServerConfiguration
* @param isInteractive
* Whether format should ask prompt for confirmation if old data
* exists or not.
* @param force
* If non interactive and force is true, then old data will be
* removed without confirm prompt.
* @return Returns true if the format is success else returns false
*/
public static boolean format(ServerConfiguration conf,
boolean isInteractive, boolean force) {
File journalDir = conf.getJournalDir();
if (journalDir.exists() && journalDir.isDirectory()
&& journalDir.list().length != 0) {
try {
boolean confirm = false;
if (!isInteractive) {
// If non interactive and force is set, then delete old
// data.
if (force) {
confirm = true;
} else {
confirm = false;
}
} else {
confirm = IOUtils
.confirmPrompt("Are you sure to format Bookie data..?");
}
if (!confirm) {
LOG.error("Bookie format aborted!!");
return false;
}
} catch (IOException e) {
LOG.error("Error during bookie format", e);
return false;
}
}
if (!cleanDir(journalDir)) {
LOG.error("Formatting journal directory failed");
return false;
}
File[] ledgerDirs = conf.getLedgerDirs();
for (File dir : ledgerDirs) {
if (!cleanDir(dir)) {
LOG.error("Formatting ledger directory " + dir + " failed");
return false;
}
}
LOG.info("Bookie format completed successfully");
return true;
}
private static boolean cleanDir(File dir) {
if (dir.exists()) {
for (File child : dir.listFiles()) {
boolean delete = FileUtils.deleteQuietly(child);
if (!delete) {
LOG.error("Not able to delete " + child);
return false;
}
}
} else if (!dir.mkdirs()) {
LOG.error("Not able to create the directory " + dir);
return false;
}
return true;
}
/**
* @param args
* @throws IOException
* @throws InterruptedException
*/
public static void main(String[] args)
throws IOException, InterruptedException, BookieException, KeeperException {
Bookie b = new Bookie(new ServerConfiguration());
b.start();
CounterCallback cb = new CounterCallback();
long start = MathUtils.now();
for (int i = 0; i < 100000; i++) {
ByteBuffer buff = ByteBuffer.allocate(1024);
buff.putLong(1);
buff.putLong(i);
buff.limit(1024);
buff.position(0);
cb.incCount();
b.addEntry(buff, cb, null, new byte[0]);
}
cb.waitZero();
long end = MathUtils.now();
System.out.println("Took " + (end-start) + "ms");
}
/**
* Returns exit code - cause of failure
*
* @return {@link ExitCode}
*/
public int getExitCode() {
return exitCode;
}
}
BookieBean.java 0000664 0000000 0000000 00000002510 12445073612 0033545 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /**
* 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.bookie;
import java.io.File;
import org.apache.bookkeeper.jmx.BKMBeanInfo;
/**
* Bookie Bean
*/
public class BookieBean implements BookieMXBean, BKMBeanInfo {
protected Bookie bk;
public BookieBean(Bookie bk) {
this.bk = bk;
}
@Override
public String getName() {
return "Bookie";
}
@Override
public boolean isHidden() {
return false;
}
@Override
public int getQueueLength() {
return bk.journal.getJournalQueueLength();
}
}
BookieException.java 0000664 0000000 0000000 00000010641 12445073612 0034642 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie package org.apache.bookkeeper.bookie;
/*
*
* 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.lang.Exception;
@SuppressWarnings("serial")
public abstract class BookieException extends Exception {
private int code;
public BookieException(int code) {
this.code = code;
}
public BookieException(int code, Throwable t) {
super(t);
}
public BookieException(int code, String reason) {
super(reason);
}
public static BookieException create(int code) {
switch(code) {
case Code.UnauthorizedAccessException:
return new BookieUnauthorizedAccessException();
case Code.LedgerFencedException:
return new LedgerFencedException();
case Code.InvalidCookieException:
return new InvalidCookieException();
case Code.UpgradeException:
return new UpgradeException();
default:
return new BookieIllegalOpException();
}
}
public interface Code {
int OK = 0;
int UnauthorizedAccessException = -1;
int IllegalOpException = -100;
int LedgerFencedException = -101;
int InvalidCookieException = -102;
int UpgradeException = -103;
}
public void setCode(int code) {
this.code = code;
}
public int getCode() {
return this.code;
}
public String getMessage(int code) {
String err = "Invalid operation";
switch(code) {
case Code.OK:
err = "No problem";
break;
case Code.UnauthorizedAccessException:
err = "Error while reading ledger";
break;
case Code.LedgerFencedException:
err = "Ledger has been fenced; No more entries can be added";
break;
case Code.InvalidCookieException:
err = "Invalid environment cookie found";
break;
case Code.UpgradeException:
err = "Error performing an upgrade operation ";
break;
}
String reason = super.getMessage();
if (reason == null) {
if (super.getCause() != null) {
reason = super.getCause().getMessage();
}
}
if (reason == null) {
return err;
} else {
return String.format("%s [%s]", err, reason);
}
}
public static class BookieUnauthorizedAccessException extends BookieException {
public BookieUnauthorizedAccessException() {
super(Code.UnauthorizedAccessException);
}
}
public static class BookieIllegalOpException extends BookieException {
public BookieIllegalOpException() {
super(Code.UnauthorizedAccessException);
}
}
public static class LedgerFencedException extends BookieException {
public LedgerFencedException() {
super(Code.LedgerFencedException);
}
}
public static class InvalidCookieException extends BookieException {
public InvalidCookieException() {
this("");
}
public InvalidCookieException(String reason) {
super(Code.InvalidCookieException, reason);
}
public InvalidCookieException(Throwable cause) {
super(Code.InvalidCookieException, cause);
}
}
public static class UpgradeException extends BookieException {
public UpgradeException() {
super(Code.UpgradeException);
}
public UpgradeException(Throwable cause) {
super(Code.UpgradeException, cause);
}
public UpgradeException(String reason) {
super(Code.UpgradeException, reason);
}
}
}
BookieMXBean.java 0000664 0000000 0000000 00000001766 12445073612 0034026 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /**
* 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.bookie;
import java.io.File;
/**
* Bookie MBean
*/
public interface BookieMXBean {
/**
* @return log entry queue length
*/
public int getQueueLength();
}
BookieShell.java 0000664 0000000 0000000 00000126551 12445073612 0033763 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /**
* 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.bookie;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import org.apache.zookeeper.ZooKeeper;
import org.apache.bookkeeper.meta.LedgerManagerFactory;
import org.apache.bookkeeper.meta.LedgerUnderreplicationManager;
import org.apache.bookkeeper.zookeeper.ZooKeeperWatcherBase;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import org.apache.bookkeeper.replication.AuditorElector;
import org.apache.bookkeeper.bookie.EntryLogger.EntryLogScanner;
import org.apache.bookkeeper.bookie.Journal.JournalScanner;
import org.apache.bookkeeper.bookie.Journal.LastLogMark;
import org.apache.bookkeeper.client.BKException;
import org.apache.bookkeeper.client.BookKeeperAdmin;
import org.apache.bookkeeper.client.BookKeeper;
import org.apache.bookkeeper.client.LedgerHandle;
import org.apache.bookkeeper.client.LedgerMetadata;
import org.apache.bookkeeper.conf.ClientConfiguration;
import org.apache.bookkeeper.conf.ServerConfiguration;
import org.apache.bookkeeper.meta.LedgerManager;
import org.apache.bookkeeper.meta.LedgerManager.LedgerRangeIterator;
import org.apache.bookkeeper.meta.LedgerManager.LedgerRange;
import org.apache.bookkeeper.util.EntryFormatter;
import org.apache.bookkeeper.util.Tool;
import org.apache.bookkeeper.util.ZkUtils;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.GenericCallback;
import com.google.common.util.concurrent.AbstractFuture;
import static com.google.common.base.Charsets.UTF_8;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.MissingArgumentException;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Bookie Shell is to provide utilities for users to administer a bookkeeper cluster.
*/
public class BookieShell implements Tool {
static final Logger LOG = LoggerFactory.getLogger(BookieShell.class);
static final String ENTRY_FORMATTER_CLASS = "entryFormatterClass";
static final String CMD_METAFORMAT = "metaformat";
static final String CMD_BOOKIEFORMAT = "bookieformat";
static final String CMD_RECOVER = "recover";
static final String CMD_LEDGER = "ledger";
static final String CMD_LISTLEDGERS = "listledgers";
static final String CMD_LEDGERMETADATA = "ledgermetadata";
static final String CMD_LISTUNDERREPLICATED = "listunderreplicated";
static final String CMD_WHOISAUDITOR = "whoisauditor";
static final String CMD_SIMPLETEST = "simpletest";
static final String CMD_READLOG = "readlog";
static final String CMD_READJOURNAL = "readjournal";
static final String CMD_LASTMARK = "lastmark";
static final String CMD_AUTORECOVERY = "autorecovery";
static final String CMD_HELP = "help";
final ServerConfiguration bkConf = new ServerConfiguration();
File[] ledgerDirectories;
File journalDirectory;
EntryLogger entryLogger = null;
Journal journal = null;
EntryFormatter formatter;
int pageSize;
int entriesPerPage;
interface Command {
public int runCmd(String[] args) throws Exception;
public void printUsage();
}
abstract class MyCommand implements Command {
abstract Options getOptions();
abstract String getDescription();
abstract String getUsage();
abstract int runCmd(CommandLine cmdLine) throws Exception;
String cmdName;
MyCommand(String cmdName) {
this.cmdName = cmdName;
}
@Override
public int runCmd(String[] args) throws Exception {
try {
BasicParser parser = new BasicParser();
CommandLine cmdLine = parser.parse(getOptions(), args);
return runCmd(cmdLine);
} catch (ParseException e) {
LOG.error("Error parsing command line arguments : ", e);
printUsage();
return -1;
}
}
@Override
public void printUsage() {
HelpFormatter hf = new HelpFormatter();
System.err.println(cmdName + ": " + getDescription());
hf.printHelp(getUsage(), getOptions());
}
}
/**
* Format the bookkeeper metadata present in zookeeper
*/
class MetaFormatCmd extends MyCommand {
Options opts = new Options();
MetaFormatCmd() {
super(CMD_METAFORMAT);
opts.addOption("n", "nonInteractive", false,
"Whether to confirm if old data exists..?");
opts.addOption("f", "force", false,
"If [nonInteractive] is specified, then whether"
+ " to force delete the old data without prompt.");
}
@Override
Options getOptions() {
return opts;
}
@Override
String getDescription() {
return "Format bookkeeper metadata in zookeeper";
}
@Override
String getUsage() {
return "metaformat [-nonInteractive] [-force]";
}
@Override
int runCmd(CommandLine cmdLine) throws Exception {
boolean interactive = (!cmdLine.hasOption("n"));
boolean force = cmdLine.hasOption("f");
ClientConfiguration adminConf = new ClientConfiguration(bkConf);
boolean result = BookKeeperAdmin.format(adminConf, interactive,
force);
return (result) ? 0 : 1;
}
}
/**
* Formats the local data present in current bookie server
*/
class BookieFormatCmd extends MyCommand {
Options opts = new Options();
public BookieFormatCmd() {
super(CMD_BOOKIEFORMAT);
opts.addOption("n", "nonInteractive", false,
"Whether to confirm if old data exists..?");
opts.addOption("f", "force", false,
"If [nonInteractive] is specified, then whether"
+ " to force delete the old data without prompt..?");
}
@Override
Options getOptions() {
return opts;
}
@Override
String getDescription() {
return "Format the current server contents";
}
@Override
String getUsage() {
return "bookieformat [-nonInteractive] [-force]";
}
@Override
int runCmd(CommandLine cmdLine) throws Exception {
boolean interactive = (!cmdLine.hasOption("n"));
boolean force = cmdLine.hasOption("f");
ServerConfiguration conf = new ServerConfiguration(bkConf);
boolean result = Bookie.format(conf, interactive, force);
return (result) ? 0 : 1;
}
}
/**
* Recover command for ledger data recovery for failed bookie
*/
class RecoverCmd extends MyCommand {
Options opts = new Options();
public RecoverCmd() {
super(CMD_RECOVER);
}
@Override
Options getOptions() {
return opts;
}
@Override
String getDescription() {
return "Recover the ledger data for failed bookie";
}
@Override
String getUsage() {
return "recover [bookieDest]";
}
@Override
int runCmd(CommandLine cmdLine) throws Exception {
String[] args = cmdLine.getArgs();
if (args.length < 1) {
throw new MissingArgumentException(
"'bookieSrc' argument required");
}
ClientConfiguration adminConf = new ClientConfiguration(bkConf);
BookKeeperAdmin admin = new BookKeeperAdmin(adminConf);
try {
return bkRecovery(admin, args);
} finally {
if (null != admin) {
admin.close();
}
}
}
private int bkRecovery(BookKeeperAdmin bkAdmin, String[] args)
throws InterruptedException, BKException {
final String bookieSrcString[] = args[0].split(":");
if (bookieSrcString.length != 2) {
System.err.println("BookieSrc inputted has invalid format"
+ "(host:port expected): " + args[0]);
return -1;
}
final InetSocketAddress bookieSrc = new InetSocketAddress(
bookieSrcString[0], Integer.parseInt(bookieSrcString[1]));
InetSocketAddress bookieDest = null;
if (args.length >= 2) {
final String bookieDestString[] = args[1].split(":");
if (bookieDestString.length < 2) {
System.err.println("BookieDest inputted has invalid format"
+ "(host:port expected): " + args[1]);
return -1;
}
bookieDest = new InetSocketAddress(bookieDestString[0],
Integer.parseInt(bookieDestString[1]));
}
bkAdmin.recoverBookieData(bookieSrc, bookieDest);
return 0;
}
}
/**
* Ledger Command Handles ledger related operations
*/
class LedgerCmd extends MyCommand {
Options lOpts = new Options();
LedgerCmd() {
super(CMD_LEDGER);
lOpts.addOption("m", "meta", false, "Print meta information");
}
@Override
public int runCmd(CommandLine cmdLine) throws Exception {
String[] leftArgs = cmdLine.getArgs();
if (leftArgs.length <= 0) {
System.err.println("ERROR: missing ledger id");
printUsage();
return -1;
}
boolean printMeta = false;
if (cmdLine.hasOption("m")) {
printMeta = true;
}
long ledgerId;
try {
ledgerId = Long.parseLong(leftArgs[0]);
} catch (NumberFormatException nfe) {
System.err.println("ERROR: invalid ledger id " + leftArgs[0]);
printUsage();
return -1;
}
if (printMeta) {
// print meta
readLedgerMeta(ledgerId);
}
// dump ledger info
readLedgerIndexEntries(ledgerId);
return 0;
}
@Override
String getDescription() {
return "Dump ledger index entries into readable format.";
}
@Override
String getUsage() {
return "ledger [-m] ";
}
@Override
Options getOptions() {
return lOpts;
}
}
/**
* Command for listing underreplicated ledgers
*/
class ListUnderreplicatedCmd extends MyCommand {
Options opts = new Options();
public ListUnderreplicatedCmd() {
super(CMD_LISTUNDERREPLICATED);
}
@Override
Options getOptions() {
return opts;
}
@Override
String getDescription() {
return "List ledgers marked as underreplicated";
}
@Override
String getUsage() {
return "listunderreplicated";
}
@Override
int runCmd(CommandLine cmdLine) throws Exception {
ZooKeeper zk = null;
try {
ZooKeeperWatcherBase w = new ZooKeeperWatcherBase(bkConf.getZkTimeout());
zk = ZkUtils.createConnectedZookeeperClient(bkConf.getZkServers(), w);
LedgerManagerFactory mFactory = LedgerManagerFactory.newLedgerManagerFactory(bkConf, zk);
LedgerUnderreplicationManager underreplicationManager = mFactory.newLedgerUnderreplicationManager();
Iterator iter = underreplicationManager.listLedgersToRereplicate();
while (iter.hasNext()) {
System.out.println(iter.next());
}
} finally {
if (zk != null) {
zk.close();
}
}
return 0;
}
}
final static int LIST_BATCH_SIZE = 1000;
/**
* Command to list all ledgers in the cluster
*/
class ListLedgersCmd extends MyCommand {
Options lOpts = new Options();
ListLedgersCmd() {
super(CMD_LISTLEDGERS);
lOpts.addOption("m", "meta", false, "Print metadata");
}
@Override
public int runCmd(CommandLine cmdLine) throws Exception {
ZooKeeper zk = null;
try {
ZooKeeperWatcherBase w = new ZooKeeperWatcherBase(bkConf.getZkTimeout());
zk = ZkUtils.createConnectedZookeeperClient(bkConf.getZkServers(), w);
LedgerManagerFactory mFactory = LedgerManagerFactory.newLedgerManagerFactory(bkConf, zk);
LedgerManager m = mFactory.newLedgerManager();
LedgerRangeIterator iter = m.getLedgerRanges();
if (cmdLine.hasOption("m")) {
List futures
= new ArrayList(LIST_BATCH_SIZE);
while (iter.hasNext()) {
LedgerRange r = iter.next();
for (Long lid : r.getLedgers()) {
ReadMetadataCallback cb = new ReadMetadataCallback(lid);
m.readLedgerMetadata(lid, cb);
futures.add(cb);
}
if (futures.size() >= LIST_BATCH_SIZE) {
while (futures.size() > 0) {
ReadMetadataCallback cb = futures.remove(0);
printLedgerMetadata(cb);
}
}
}
while (futures.size() > 0) {
ReadMetadataCallback cb = futures.remove(0);
printLedgerMetadata(cb);
}
} else {
while (iter.hasNext()) {
LedgerRange r = iter.next();
for (Long lid : r.getLedgers()) {
System.out.println(Long.toString(lid));
}
}
}
} finally {
if (zk != null) {
zk.close();
}
}
return 0;
}
@Override
String getDescription() {
return "List all ledgers on the cluster (this may take a long time)";
}
@Override
String getUsage() {
return "listledgers [-meta]";
}
@Override
Options getOptions() {
return lOpts;
}
}
static void printLedgerMetadata(ReadMetadataCallback cb) throws Exception {
LedgerMetadata md = cb.get();
System.out.println("ledgerID: " + cb.getLedgerId());
System.out.println(new String(md.serialize(), UTF_8));
}
static class ReadMetadataCallback extends AbstractFuture
implements GenericCallback {
final long ledgerId;
ReadMetadataCallback(long ledgerId) {
this.ledgerId = ledgerId;
}
long getLedgerId() {
return ledgerId;
}
public void operationComplete(int rc, LedgerMetadata result) {
if (rc != 0) {
setException(BKException.create(rc));
} else {
set(result);
}
}
}
/**
* Print the metadata for a ledger
*/
class LedgerMetadataCmd extends MyCommand {
Options lOpts = new Options();
LedgerMetadataCmd() {
super(CMD_LEDGERMETADATA);
lOpts.addOption("l", "ledgerid", true, "Ledger ID");
}
@Override
public int runCmd(CommandLine cmdLine) throws Exception {
final long lid = getOptionLongValue(cmdLine, "ledgerid", -1);
if (lid == -1) {
System.err.println("Must specify a ledger id");
return -1;
}
ZooKeeper zk = null;
try {
ZooKeeperWatcherBase w = new ZooKeeperWatcherBase(bkConf.getZkTimeout());
zk = ZkUtils.createConnectedZookeeperClient(bkConf.getZkServers(), w);
LedgerManagerFactory mFactory = LedgerManagerFactory.newLedgerManagerFactory(bkConf, zk);
LedgerManager m = mFactory.newLedgerManager();
ReadMetadataCallback cb = new ReadMetadataCallback(lid);
m.readLedgerMetadata(lid, cb);
printLedgerMetadata(cb);
} finally {
if (zk != null) {
zk.close();
}
}
return 0;
}
@Override
String getDescription() {
return "Print the metadata for a ledger";
}
@Override
String getUsage() {
return "ledgermetadata -ledgerid ";
}
@Override
Options getOptions() {
return lOpts;
}
}
/**
* Simple test to create a ledger and write to it
*/
class SimpleTestCmd extends MyCommand {
Options lOpts = new Options();
SimpleTestCmd() {
super(CMD_SIMPLETEST);
lOpts.addOption("e", "ensemble", true, "Ensemble size (default 3)");
lOpts.addOption("w", "writeQuorum", true, "Write quorum size (default 2)");
lOpts.addOption("a", "ackQuorum", true, "Ack quorum size (default 2)");
lOpts.addOption("n", "numEntries", true, "Entries to write (default 1000)");
}
@Override
public int runCmd(CommandLine cmdLine) throws Exception {
byte[] data = new byte[100]; // test data
int ensemble = getOptionIntValue(cmdLine, "ensemble", 3);
int writeQuorum = getOptionIntValue(cmdLine, "writeQuorum", 2);
int ackQuorum = getOptionIntValue(cmdLine, "ackQuorum", 2);
int numEntries = getOptionIntValue(cmdLine, "numEntries", 1000);
ClientConfiguration conf = new ClientConfiguration();
conf.addConfiguration(bkConf);
BookKeeper bk = new BookKeeper(conf);
LedgerHandle lh = bk.createLedger(ensemble, writeQuorum, ackQuorum,
BookKeeper.DigestType.MAC, new byte[0]);
System.out.println("Ledger ID: " + lh.getId());
long lastReport = System.nanoTime();
for (int i = 0; i < numEntries; i++) {
lh.addEntry(data);
if (TimeUnit.SECONDS.convert(System.nanoTime() - lastReport,
TimeUnit.NANOSECONDS) > 1) {
System.out.println(i + " entries written");
lastReport = System.nanoTime();
}
}
lh.close();
bk.close();
System.out.println(numEntries + " entries written to ledger " + lh.getId());
return 0;
}
@Override
String getDescription() {
return "Simple test to create a ledger and write entries to it";
}
@Override
String getUsage() {
return "simpletest [-ensemble N] [-writeQuorum N] [-ackQuorum N] [-numEntries N]";
}
@Override
Options getOptions() {
return lOpts;
}
}
/**
* Command to read entry log files.
*/
class ReadLogCmd extends MyCommand {
Options rlOpts = new Options();
ReadLogCmd() {
super(CMD_READLOG);
rlOpts.addOption("m", "msg", false, "Print message body");
}
@Override
public int runCmd(CommandLine cmdLine) throws Exception {
String[] leftArgs = cmdLine.getArgs();
if (leftArgs.length <= 0) {
System.err.println("ERROR: missing entry log id or entry log file name");
printUsage();
return -1;
}
boolean printMsg = false;
if (cmdLine.hasOption("m")) {
printMsg = true;
}
long logId;
try {
logId = Long.parseLong(leftArgs[0]);
} catch (NumberFormatException nfe) {
// not a entry log id
File f = new File(leftArgs[0]);
String name = f.getName();
if (!name.endsWith(".log")) {
// not a log file
System.err.println("ERROR: invalid entry log file name " + leftArgs[0]);
printUsage();
return -1;
}
String idString = name.split("\\.")[0];
logId = Long.parseLong(idString, 16);
}
// scan entry log
scanEntryLog(logId, printMsg);
return 0;
}
@Override
String getDescription() {
return "Scan an entry file and format the entries into readable format.";
}
@Override
String getUsage() {
return "readlog [-msg] ";
}
@Override
Options getOptions() {
return rlOpts;
}
}
/**
* Command to read journal files
*/
class ReadJournalCmd extends MyCommand {
Options rjOpts = new Options();
ReadJournalCmd() {
super(CMD_READJOURNAL);
rjOpts.addOption("m", "msg", false, "Print message body");
}
@Override
public int runCmd(CommandLine cmdLine) throws Exception {
String[] leftArgs = cmdLine.getArgs();
if (leftArgs.length <= 0) {
System.err.println("ERROR: missing journal id or journal file name");
printUsage();
return -1;
}
boolean printMsg = false;
if (cmdLine.hasOption("m")) {
printMsg = true;
}
long journalId;
try {
journalId = Long.parseLong(leftArgs[0]);
} catch (NumberFormatException nfe) {
// not a journal id
File f = new File(leftArgs[0]);
String name = f.getName();
if (!name.endsWith(".txn")) {
// not a journal file
System.err.println("ERROR: invalid journal file name " + leftArgs[0]);
printUsage();
return -1;
}
String idString = name.split("\\.")[0];
journalId = Long.parseLong(idString, 16);
}
// scan journal
scanJournal(journalId, printMsg);
return 0;
}
@Override
String getDescription() {
return "Scan a journal file and format the entries into readable format.";
}
@Override
String getUsage() {
return "readjournal [-msg] ";
}
@Override
Options getOptions() {
return rjOpts;
}
}
/**
* Command to print last log mark
*/
class LastMarkCmd extends MyCommand {
LastMarkCmd() {
super(CMD_LASTMARK);
}
@Override
public int runCmd(CommandLine c) throws Exception {
printLastLogMark();
return 0;
}
@Override
String getDescription() {
return "Print last log marker.";
}
@Override
String getUsage() {
return "lastmark";
}
@Override
Options getOptions() {
return new Options();
}
}
/**
* Command to print help message
*/
class HelpCmd extends MyCommand {
HelpCmd() {
super(CMD_HELP);
}
@Override
public int runCmd(CommandLine cmdLine) throws Exception {
String[] args = cmdLine.getArgs();
if (args.length == 0) {
printShellUsage();
return 0;
}
String cmdName = args[0];
Command cmd = commands.get(cmdName);
if (null == cmd) {
System.err.println("Unknown command " + cmdName);
printShellUsage();
return -1;
}
cmd.printUsage();
return 0;
}
@Override
String getDescription() {
return "Describe the usage of this program or its subcommands.";
}
@Override
String getUsage() {
return "help [COMMAND]";
}
@Override
Options getOptions() {
return new Options();
}
}
/**
* Command for administration of autorecovery
*/
class AutoRecoveryCmd extends MyCommand {
Options opts = new Options();
public AutoRecoveryCmd() {
super(CMD_AUTORECOVERY);
opts.addOption("e", "enable", false,
"Enable auto recovery of underreplicated ledgers");
opts.addOption("d", "disable", false,
"Disable auto recovery of underreplicated ledgers");
}
@Override
Options getOptions() {
return opts;
}
@Override
String getDescription() {
return "Enable or disable autorecovery in the cluster.";
}
@Override
String getUsage() {
return "autorecovery [-enable|-disable]";
}
@Override
int runCmd(CommandLine cmdLine) throws Exception {
boolean disable = cmdLine.hasOption("d");
boolean enable = cmdLine.hasOption("e");
if ((!disable && !enable)
|| (enable && disable)) {
LOG.error("One and only one of -enable and -disable must be specified");
printUsage();
return 1;
}
ZooKeeper zk = null;
try {
ZooKeeperWatcherBase w = new ZooKeeperWatcherBase(bkConf.getZkTimeout());
zk = ZkUtils.createConnectedZookeeperClient(bkConf.getZkServers(), w);
LedgerManagerFactory mFactory = LedgerManagerFactory.newLedgerManagerFactory(bkConf, zk);
LedgerUnderreplicationManager underreplicationManager = mFactory.newLedgerUnderreplicationManager();
if (enable) {
if (underreplicationManager.isLedgerReplicationEnabled()) {
LOG.warn("Autorecovery already enabled. Doing nothing");
} else {
LOG.info("Enabling autorecovery");
underreplicationManager.enableLedgerReplication();
}
} else {
if (!underreplicationManager.isLedgerReplicationEnabled()) {
LOG.warn("Autorecovery already disabled. Doing nothing");
} else {
LOG.info("Disabling autorecovery");
underreplicationManager.disableLedgerReplication();
}
}
} finally {
if (zk != null) {
zk.close();
}
}
return 0;
}
}
/**
* Print which node has the auditor lock
*/
class WhoIsAuditorCmd extends MyCommand {
Options opts = new Options();
public WhoIsAuditorCmd() {
super(CMD_WHOISAUDITOR);
}
@Override
Options getOptions() {
return opts;
}
@Override
String getDescription() {
return "Print the node which holds the auditor lock";
}
@Override
String getUsage() {
return "whoisauditor";
}
@Override
int runCmd(CommandLine cmdLine) throws Exception {
ZooKeeper zk = null;
try {
ZooKeeperWatcherBase w = new ZooKeeperWatcherBase(bkConf.getZkTimeout());
zk = ZkUtils.createConnectedZookeeperClient(bkConf.getZkServers(), w);
InetSocketAddress bookieId = AuditorElector.getCurrentAuditor(bkConf, zk);
if (bookieId == null) {
LOG.info("No auditor elected");
return -1;
}
LOG.info("Auditor: {}/{}:{}",
new Object[] {
bookieId.getAddress().getCanonicalHostName(),
bookieId.getAddress().getHostAddress(),
bookieId.getPort() });
} finally {
if (zk != null) {
zk.close();
}
}
return 0;
}
}
final Map commands = new HashMap();
{
commands.put(CMD_METAFORMAT, new MetaFormatCmd());
commands.put(CMD_BOOKIEFORMAT, new BookieFormatCmd());
commands.put(CMD_RECOVER, new RecoverCmd());
commands.put(CMD_LEDGER, new LedgerCmd());
commands.put(CMD_LISTLEDGERS, new ListLedgersCmd());
commands.put(CMD_LISTUNDERREPLICATED, new ListUnderreplicatedCmd());
commands.put(CMD_WHOISAUDITOR, new WhoIsAuditorCmd());
commands.put(CMD_LEDGERMETADATA, new LedgerMetadataCmd());
commands.put(CMD_SIMPLETEST, new SimpleTestCmd());
commands.put(CMD_READLOG, new ReadLogCmd());
commands.put(CMD_READJOURNAL, new ReadJournalCmd());
commands.put(CMD_LASTMARK, new LastMarkCmd());
commands.put(CMD_AUTORECOVERY, new AutoRecoveryCmd());
commands.put(CMD_HELP, new HelpCmd());
}
@Override
public void setConf(Configuration conf) throws Exception {
bkConf.loadConf(conf);
journalDirectory = Bookie.getCurrentDirectory(bkConf.getJournalDir());
ledgerDirectories = Bookie.getCurrentDirectories(bkConf.getLedgerDirs());
formatter = EntryFormatter.newEntryFormatter(bkConf, ENTRY_FORMATTER_CLASS);
LOG.info("Using entry formatter " + formatter.getClass().getName());
pageSize = bkConf.getPageSize();
entriesPerPage = pageSize / 8;
}
private void printShellUsage() {
System.err.println("Usage: BookieShell [-conf configuration] ");
System.err.println();
List commandNames = new ArrayList();
for (MyCommand c : commands.values()) {
commandNames.add(" " + c.getUsage());
}
Collections.sort(commandNames);
for (String s : commandNames) {
System.err.println(s);
}
}
@Override
public int run(String[] args) throws Exception {
if (args.length <= 0) {
printShellUsage();
return -1;
}
String cmdName = args[0];
Command cmd = commands.get(cmdName);
if (null == cmd) {
System.err.println("ERROR: Unknown command " + cmdName);
printShellUsage();
return -1;
}
// prepare new args
String[] newArgs = new String[args.length - 1];
System.arraycopy(args, 1, newArgs, 0, newArgs.length);
return cmd.runCmd(newArgs);
}
public static void main(String argv[]) throws Exception {
BookieShell shell = new BookieShell();
if (argv.length <= 0) {
shell.printShellUsage();
System.exit(-1);
}
CompositeConfiguration conf = new CompositeConfiguration();
// load configuration
if ("-conf".equals(argv[0])) {
if (argv.length <= 1) {
shell.printShellUsage();
System.exit(-1);
}
conf.addConfiguration(new PropertiesConfiguration(
new File(argv[1]).toURI().toURL()));
String[] newArgv = new String[argv.length - 2];
System.arraycopy(argv, 2, newArgv, 0, newArgv.length);
argv = newArgv;
}
shell.setConf(conf);
int res = shell.run(argv);
System.exit(res);
}
///
/// Bookie File Operations
///
/**
* Get the ledger file of a specified ledger.
*
* @param ledgerId
* Ledger Id
*
* @return file object.
*/
private File getLedgerFile(long ledgerId) {
String ledgerName = LedgerCacheImpl.getLedgerName(ledgerId);
File lf = null;
for (File d : ledgerDirectories) {
lf = new File(d, ledgerName);
if (lf.exists()) {
break;
}
lf = null;
}
return lf;
}
/**
* Get FileInfo for a specified ledger.
*
* @param ledgerId
* Ledger Id
* @return read only file info instance
*/
ReadOnlyFileInfo getFileInfo(long ledgerId) throws IOException {
File ledgerFile = getLedgerFile(ledgerId);
if (null == ledgerFile) {
throw new FileNotFoundException("No index file found for ledger " + ledgerId + ". It may be not flushed yet.");
}
ReadOnlyFileInfo fi = new ReadOnlyFileInfo(ledgerFile, null);
fi.readHeader();
return fi;
}
private synchronized void initEntryLogger() throws IOException {
if (null == entryLogger) {
// provide read only entry logger
entryLogger = new ReadOnlyEntryLogger(bkConf);
}
}
/**
* scan over entry log
*
* @param logId
* Entry Log Id
* @param scanner
* Entry Log Scanner
*/
protected void scanEntryLog(long logId, EntryLogScanner scanner) throws IOException {
initEntryLogger();
entryLogger.scanEntryLog(logId, scanner);
}
private synchronized Journal getJournal() throws IOException {
if (null == journal) {
journal = new Journal(bkConf, new LedgerDirsManager(bkConf));
}
return journal;
}
/**
* Scan journal file
*
* @param journalId
* Journal File Id
* @param scanner
* Journal File Scanner
*/
protected void scanJournal(long journalId, JournalScanner scanner) throws IOException {
getJournal().scanJournal(journalId, 0L, scanner);
}
///
/// Bookie Shell Commands
///
/**
* Read ledger meta
*
* @param ledgerId
* Ledger Id
*/
protected void readLedgerMeta(long ledgerId) throws Exception {
System.out.println("===== LEDGER: " + ledgerId + " =====");
FileInfo fi = getFileInfo(ledgerId);
byte[] masterKey = fi.getMasterKey();
if (null == masterKey) {
System.out.println("master key : NULL");
} else {
System.out.println("master key : " + bytes2Hex(fi.getMasterKey()));
}
long size = fi.size();
if (size % 8 == 0) {
System.out.println("size : " + size);
} else {
System.out.println("size : " + size + " (not aligned with 8, may be corrupted or under flushing now)");
}
System.out.println("entries : " + (size / 8));
}
/**
* Read ledger index entires
*
* @param ledgerId
* Ledger Id
* @throws IOException
*/
protected void readLedgerIndexEntries(long ledgerId) throws IOException {
System.out.println("===== LEDGER: " + ledgerId + " =====");
FileInfo fi = getFileInfo(ledgerId);
long size = fi.size();
System.out.println("size : " + size);
long curSize = 0;
long curEntry = 0;
LedgerEntryPage lep = new LedgerEntryPage(pageSize, entriesPerPage);
lep.usePage();
try {
while (curSize < size) {
lep.setLedger(ledgerId);
lep.setFirstEntry(curEntry);
lep.readPage(fi);
// process a page
for (int i=0; i> 32L;
long pos = offset & 0xffffffffL;
System.out.println("entry " + curEntry + "\t:\t(log:" + entryLogId + ", pos: " + pos + ")");
}
++curEntry;
}
curSize += pageSize;
}
} catch (IOException ie) {
LOG.error("Failed to read index page : ", ie);
if (curSize + pageSize < size) {
System.out.println("Failed to read index page @ " + curSize + ", the index file may be corrupted : " + ie.getMessage());
} else {
System.out.println("Failed to read last index page @ " + curSize
+ ", the index file may be corrupted or last index page is not fully flushed yet : " + ie.getMessage());
}
}
}
/**
* Scan over an entry log file.
*
* @param logId
* Entry Log File id.
* @param printMsg
* Whether printing the entry data.
*/
protected void scanEntryLog(long logId, final boolean printMsg) throws Exception {
System.out.println("Scan entry log " + logId + " (" + Long.toHexString(logId) + ".log)");
scanEntryLog(logId, new EntryLogScanner() {
@Override
public boolean accept(long ledgerId) {
return true;
}
@Override
public void process(long ledgerId, long startPos, ByteBuffer entry) {
formatEntry(startPos, entry, printMsg);
}
});
}
/**
* Scan a journal file
*
* @param journalId
* Journal File Id
* @param printMsg
* Whether printing the entry data.
*/
protected void scanJournal(long journalId, final boolean printMsg) throws Exception {
System.out.println("Scan journal " + journalId + " (" + Long.toHexString(journalId) + ".txn)");
scanJournal(journalId, new JournalScanner() {
boolean printJournalVersion = false;
@Override
public void process(int journalVersion, long offset, ByteBuffer entry) throws IOException {
if (!printJournalVersion) {
System.out.println("Journal Version : " + journalVersion);
printJournalVersion = true;
}
formatEntry(offset, entry, printMsg);
}
});
}
/**
* Print last log mark
*/
protected void printLastLogMark() throws IOException {
LastLogMark lastLogMark = getJournal().getLastLogMark();
System.out.println("LastLogMark: Journal Id - " + lastLogMark.getTxnLogId() + "("
+ Long.toHexString(lastLogMark.getTxnLogId()) + ".txn), Pos - "
+ lastLogMark.getTxnLogPosition());
}
/**
* Format the message into a readable format.
*
* @param pos
* File offset of the message stored in entry log file
* @param recBuff
* Entry Data
* @param printMsg
* Whether printing the message body
*/
private void formatEntry(long pos, ByteBuffer recBuff, boolean printMsg) {
long ledgerId = recBuff.getLong();
long entryId = recBuff.getLong();
int entrySize = recBuff.limit();
System.out.println("--------- Lid=" + ledgerId + ", Eid=" + entryId
+ ", ByteOffset=" + pos + ", EntrySize=" + entrySize + " ---------");
if (entryId == Bookie.METAENTRY_ID_LEDGER_KEY) {
int masterKeyLen = recBuff.getInt();
byte[] masterKey = new byte[masterKeyLen];
recBuff.get(masterKey);
System.out.println("Type: META");
System.out.println("MasterKey: " + bytes2Hex(masterKey));
System.out.println();
return;
}
if (entryId == Bookie.METAENTRY_ID_FENCE_KEY) {
System.out.println("Type: META");
System.out.println("Fenced");
System.out.println();
return;
}
// process a data entry
long lastAddConfirmed = recBuff.getLong();
System.out.println("Type: DATA");
System.out.println("LastConfirmed: " + lastAddConfirmed);
if (!printMsg) {
System.out.println();
return;
}
// skip digest checking
recBuff.position(32 + 8);
System.out.println("Data:");
System.out.println();
try {
byte[] ret = new byte[recBuff.remaining()];
recBuff.get(ret);
formatter.formatEntry(ret);
} catch (Exception e) {
System.out.println("N/A. Corrupted.");
}
System.out.println();
}
static String bytes2Hex(byte[] data) {
StringBuilder sb = new StringBuilder(data.length * 2);
Formatter formatter = new Formatter(sb);
for (byte b : data) {
formatter.format("%02x", b);
}
return sb.toString();
}
private static int getOptionIntValue(CommandLine cmdLine, String option, int defaultVal) {
if (cmdLine.hasOption(option)) {
String val = cmdLine.getOptionValue(option);
try {
return Integer.parseInt(val);
} catch (NumberFormatException nfe) {
System.err.println("ERROR: invalid value for option " + option + " : " + val);
return defaultVal;
}
}
return defaultVal;
}
private static long getOptionLongValue(CommandLine cmdLine, String option, long defaultVal) {
if (cmdLine.hasOption(option)) {
String val = cmdLine.getOptionValue(option);
try {
return Long.parseLong(val);
} catch (NumberFormatException nfe) {
System.err.println("ERROR: invalid value for option " + option + " : " + val);
return defaultVal;
}
}
return defaultVal;
}
}
BufferedChannel.java 0000664 0000000 0000000 00000014455 12445073612 0034575 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /*
*
* 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.bookie;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* Provides a buffering layer in front of a FileChannel.
*/
public class BufferedChannel
{
ByteBuffer writeBuffer;
ByteBuffer readBuffer;
private FileChannel bc;
long position;
int capacity;
long readBufferStartPosition;
long writeBufferStartPosition;
// make constructor to be public for unit test
public BufferedChannel(FileChannel bc, int capacity) throws IOException {
this.bc = bc;
this.capacity = capacity;
position = bc.position();
writeBufferStartPosition = position;
}
/**
* @return file channel
*/
FileChannel getFileChannel() {
return this.bc;
}
/* public void close() throws IOException {
bc.close();
}
*/
// public boolean isOpen() {
// return bc.isOpen();
// }
synchronized public int write(ByteBuffer src) throws IOException {
int copied = 0;
if (writeBuffer == null) {
writeBuffer = ByteBuffer.allocateDirect(capacity);
}
while(src.remaining() > 0) {
int truncated = 0;
if (writeBuffer.remaining() < src.remaining()) {
truncated = src.remaining() - writeBuffer.remaining();
src.limit(src.limit()-truncated);
}
copied += src.remaining();
writeBuffer.put(src);
src.limit(src.limit()+truncated);
if (writeBuffer.remaining() == 0) {
writeBuffer.flip();
bc.write(writeBuffer);
writeBuffer.clear();
writeBufferStartPosition = bc.position();
}
}
position += copied;
return copied;
}
public long position() {
return position;
}
/**
* Retrieve the current size of the underlying FileChannel
*
* @return FileChannel size measured in bytes
*
* @throws IOException if some I/O error occurs reading the FileChannel
*/
public long size() throws IOException {
return bc.size();
}
public void flush(boolean sync) throws IOException {
synchronized(this) {
if (writeBuffer == null) {
return;
}
writeBuffer.flip();
bc.write(writeBuffer);
writeBuffer.clear();
writeBufferStartPosition = bc.position();
}
if (sync) {
bc.force(false);
}
}
/*public Channel getInternalChannel() {
return bc;
}*/
synchronized public int read(ByteBuffer buff, long pos) throws IOException {
if (readBuffer == null) {
readBuffer = ByteBuffer.allocateDirect(capacity);
readBufferStartPosition = Long.MIN_VALUE;
}
long prevPos = pos;
while(buff.remaining() > 0) {
// check if it is in the write buffer
if (writeBuffer != null && writeBufferStartPosition <= pos) {
long positionInBuffer = pos - writeBufferStartPosition;
long bytesToCopy = writeBuffer.position()-positionInBuffer;
if (bytesToCopy > buff.remaining()) {
bytesToCopy = buff.remaining();
}
if (bytesToCopy == 0) {
throw new IOException("Read past EOF");
}
ByteBuffer src = writeBuffer.duplicate();
src.position((int) positionInBuffer);
src.limit((int) (positionInBuffer+bytesToCopy));
buff.put(src);
pos+= bytesToCopy;
} else if (writeBuffer == null && writeBufferStartPosition <= pos) {
// here we reach the end
break;
// first check if there is anything we can grab from the readBuffer
} else if (readBufferStartPosition <= pos && pos < readBufferStartPosition+readBuffer.capacity()) {
long positionInBuffer = pos - readBufferStartPosition;
long bytesToCopy = readBuffer.capacity()-positionInBuffer;
if (bytesToCopy > buff.remaining()) {
bytesToCopy = buff.remaining();
}
ByteBuffer src = readBuffer.duplicate();
src.position((int) positionInBuffer);
src.limit((int) (positionInBuffer+bytesToCopy));
buff.put(src);
pos += bytesToCopy;
// let's read it
} else {
readBufferStartPosition = pos;
readBuffer.clear();
// make sure that we don't overlap with the write buffer
if (readBufferStartPosition + readBuffer.capacity() >= writeBufferStartPosition) {
readBufferStartPosition = writeBufferStartPosition - readBuffer.capacity();
if (readBufferStartPosition < 0) {
readBuffer.put(LedgerEntryPage.zeroPage, 0, (int)-readBufferStartPosition);
}
}
while(readBuffer.remaining() > 0) {
if (bc.read(readBuffer, readBufferStartPosition+readBuffer.position()) <= 0) {
throw new IOException("Short read");
}
}
readBuffer.put(LedgerEntryPage.zeroPage, 0, readBuffer.remaining());
readBuffer.clear();
}
}
return (int)(pos - prevPos);
}
}
bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/Cookie.java 0000664 0000000 0000000 00000023460 12445073612 0033046 0 ustar 00root root 0000000 0000000 /**
*
* 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.bookie;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.OutputStreamWriter;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.StringReader;
import java.net.UnknownHostException;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.bookkeeper.util.BookKeeperConstants;
import org.apache.bookkeeper.util.StringUtils;
import org.apache.bookkeeper.conf.ServerConfiguration;
import org.apache.bookkeeper.proto.DataFormats.CookieFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.protobuf.TextFormat;
/**
* When a bookie starts for the first time it generates a cookie, and stores
* the cookie in zookeeper as well as in the each of the local filesystem
* directories it uses. This cookie is used to ensure that for the life of the
* bookie, its configuration stays the same. If any of the bookie directories
* becomes unavailable, the bookie becomes unavailable. If the bookie changes
* port, it must also reset all of its data.
*
* This is done to ensure data integrity. Without the cookie a bookie could
* start with one of its ledger directories missing, so data would be missing,
* but the bookie would be up, so the client would think that everything is ok
* with the cluster. It's better to fail early and obviously.
*/
class Cookie {
static Logger LOG = LoggerFactory.getLogger(Cookie.class);
static final int CURRENT_COOKIE_LAYOUT_VERSION = 4;
private int layoutVersion = 0;
private String bookieHost = null;
private String journalDir = null;
private String ledgerDirs = null;
private int znodeVersion = -1;
private String instanceId = null;
private Cookie() {
}
public void verify(Cookie c) throws BookieException.InvalidCookieException {
String errMsg;
if (c.layoutVersion < 3 && c.layoutVersion != layoutVersion) {
errMsg = "Cookie is of too old version " + c.layoutVersion;
LOG.error(errMsg);
throw new BookieException.InvalidCookieException(errMsg);
} else if (!(c.layoutVersion >= 3 && c.bookieHost.equals(bookieHost)
&& c.journalDir.equals(journalDir) && c.ledgerDirs
.equals(ledgerDirs))) {
errMsg = "Cookie [" + this + "] is not matching with [" + c + "]";
throw new BookieException.InvalidCookieException(errMsg);
} else if ((instanceId == null && c.instanceId != null)
|| (instanceId != null && !instanceId.equals(c.instanceId))) {
// instanceId should be same in both cookies
errMsg = "instanceId " + instanceId
+ " is not matching with " + c.instanceId;
throw new BookieException.InvalidCookieException(errMsg);
}
}
public String toString() {
if (layoutVersion <= 3) {
return toStringVersion3();
}
CookieFormat.Builder builder = CookieFormat.newBuilder();
builder.setBookieHost(bookieHost);
builder.setJournalDir(journalDir);
builder.setLedgerDirs(ledgerDirs);
if (null != instanceId) {
builder.setInstanceId(instanceId);
}
StringBuilder b = new StringBuilder();
b.append(CURRENT_COOKIE_LAYOUT_VERSION).append("\n");
b.append(TextFormat.printToString(builder.build()));
return b.toString();
}
private String toStringVersion3() {
StringBuilder b = new StringBuilder();
b.append(CURRENT_COOKIE_LAYOUT_VERSION).append("\n")
.append(bookieHost).append("\n")
.append(journalDir).append("\n")
.append(ledgerDirs).append("\n");
return b.toString();
}
private static Cookie parse(BufferedReader reader) throws IOException {
Cookie c = new Cookie();
String line = reader.readLine();
if (null == line) {
throw new EOFException("Exception in parsing cookie");
}
try {
c.layoutVersion = Integer.parseInt(line.trim());
} catch (NumberFormatException e) {
throw new IOException("Invalid string '" + line.trim()
+ "', cannot parse cookie.");
}
if (c.layoutVersion == 3) {
c.bookieHost = reader.readLine();
c.journalDir = reader.readLine();
c.ledgerDirs = reader.readLine();
} else if (c.layoutVersion >= 4) {
CookieFormat.Builder builder = CookieFormat.newBuilder();
TextFormat.merge(reader, builder);
CookieFormat data = builder.build();
c.bookieHost = data.getBookieHost();
c.journalDir = data.getJournalDir();
c.ledgerDirs = data.getLedgerDirs();
// Since InstanceId is optional
if (null != data.getInstanceId() && !data.getInstanceId().isEmpty()) {
c.instanceId = data.getInstanceId();
}
}
return c;
}
void writeToDirectory(File directory) throws IOException {
File versionFile = new File(directory,
BookKeeperConstants.VERSION_FILENAME);
FileOutputStream fos = new FileOutputStream(versionFile);
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new OutputStreamWriter(fos));
bw.write(toString());
} finally {
if (bw != null) {
bw.close();
}
fos.close();
}
}
void writeToZooKeeper(ZooKeeper zk, ServerConfiguration conf)
throws KeeperException, InterruptedException, UnknownHostException {
String bookieCookiePath = conf.getZkLedgersRootPath() + "/"
+ BookKeeperConstants.COOKIE_NODE;
String zkPath = getZkPath(conf);
byte[] data = toString().getBytes();
if (znodeVersion != -1) {
zk.setData(zkPath, data, znodeVersion);
} else {
if (zk.exists(bookieCookiePath, false) == null) {
try {
zk.create(bookieCookiePath, new byte[0],
Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
} catch (KeeperException.NodeExistsException nne) {
LOG.info("More than one bookie tried to create {} at once. Safe to ignore",
bookieCookiePath);
}
}
zk.create(zkPath, data,
Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
Stat stat = zk.exists(zkPath, false);
this.znodeVersion = stat.getVersion();
}
}
void deleteFromZooKeeper(ZooKeeper zk, ServerConfiguration conf)
throws KeeperException, InterruptedException, UnknownHostException {
String zkPath = getZkPath(conf);
if (znodeVersion != -1) {
zk.delete(zkPath, znodeVersion);
}
znodeVersion = -1;
}
static Cookie generateCookie(ServerConfiguration conf)
throws UnknownHostException {
Cookie c = new Cookie();
c.layoutVersion = CURRENT_COOKIE_LAYOUT_VERSION;
c.bookieHost = StringUtils.addrToString(Bookie.getBookieAddress(conf));
c.journalDir = conf.getJournalDirName();
StringBuilder b = new StringBuilder();
String[] dirs = conf.getLedgerDirNames();
b.append(dirs.length);
for (String d : dirs) {
b.append("\t").append(d);
}
c.ledgerDirs = b.toString();
return c;
}
static Cookie readFromZooKeeper(ZooKeeper zk, ServerConfiguration conf)
throws KeeperException, InterruptedException, IOException, UnknownHostException {
String zkPath = getZkPath(conf);
Stat stat = zk.exists(zkPath, false);
byte[] data = zk.getData(zkPath, false, stat);
BufferedReader reader = new BufferedReader(new StringReader(new String(
data)));
try {
Cookie c = parse(reader);
c.znodeVersion = stat.getVersion();
return c;
} finally {
reader.close();
}
}
static Cookie readFromDirectory(File directory) throws IOException {
File versionFile = new File(directory,
BookKeeperConstants.VERSION_FILENAME);
BufferedReader reader = new BufferedReader(new FileReader(versionFile));
try {
return parse(reader);
} finally {
reader.close();
}
}
public void setInstanceId(String instanceId) {
this.instanceId = instanceId;
}
private static String getZkPath(ServerConfiguration conf)
throws UnknownHostException {
String bookieCookiePath = conf.getZkLedgersRootPath() + "/"
+ BookKeeperConstants.COOKIE_NODE;
return bookieCookiePath + "/" + StringUtils.addrToString(Bookie.getBookieAddress(conf));
}
}
EntryLogger.java 0000664 0000000 0000000 00000050673 12445073612 0034025 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /*
*
* 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.bookie;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.CopyOnWriteArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.bookkeeper.bookie.LedgerDirsManager.LedgerDirsListener;
import org.apache.bookkeeper.conf.ServerConfiguration;
import org.apache.bookkeeper.util.IOUtils;
/**
* This class manages the writing of the bookkeeper entries. All the new
* entries are written to a common log. The LedgerCache will have pointers
* into files created by this class with offsets into the files to find
* the actual ledger entry. The entry log files created by this class are
* identified by a long.
*/
public class EntryLogger {
private static final Logger LOG = LoggerFactory.getLogger(EntryLogger.class);
volatile File currentDir;
private LedgerDirsManager ledgerDirsManager;
private AtomicBoolean shouldCreateNewEntryLog = new AtomicBoolean(false);
private long logId;
/**
* The maximum size of a entry logger file.
*/
final long logSizeLimit;
private volatile BufferedChannel logChannel;
private final CopyOnWriteArrayList listeners
= new CopyOnWriteArrayList();
// this indicates that a write has happened since the last flush
private volatile boolean somethingWritten = false;
/**
* The 1K block at the head of the entry logger file
* that contains the fingerprint and (future) meta-data
*/
final static int LOGFILE_HEADER_SIZE = 1024;
final ByteBuffer LOGFILE_HEADER = ByteBuffer.allocate(LOGFILE_HEADER_SIZE);
final static int MIN_SANE_ENTRY_SIZE = 8 + 8;
final static long MB = 1024 * 1024;
/**
* Scan entries in a entry log file.
*/
static interface EntryLogScanner {
/**
* Tests whether or not the entries belongs to the specified ledger
* should be processed.
*
* @param ledgerId
* Ledger ID.
* @return true if and only the entries of the ledger should be scanned.
*/
public boolean accept(long ledgerId);
/**
* Process an entry.
*
* @param ledgerId
* Ledger ID.
* @param offset
* File offset of this entry.
* @param entry
* Entry ByteBuffer
* @throws IOException
*/
public void process(long ledgerId, long offset, ByteBuffer entry) throws IOException;
}
/**
* Entry Log Listener
*/
static interface EntryLogListener {
/**
* Callback when entry log is flushed.
*/
public void onEntryLogFlushed();
}
/**
* Create an EntryLogger that stores it's log files in the given
* directories
*/
public EntryLogger(ServerConfiguration conf,
LedgerDirsManager ledgerDirsManager) throws IOException {
this.ledgerDirsManager = ledgerDirsManager;
// log size limit
this.logSizeLimit = conf.getEntryLogSizeLimit();
// Initialize the entry log header buffer. This cannot be a static object
// since in our unit tests, we run multiple Bookies and thus EntryLoggers
// within the same JVM. All of these Bookie instances access this header
// so there can be race conditions when entry logs are rolled over and
// this header buffer is cleared before writing it into the new logChannel.
LOGFILE_HEADER.put("BKLO".getBytes());
// Find the largest logId
logId = -1;
for (File dir : ledgerDirsManager.getAllLedgerDirs()) {
if (!dir.exists()) {
throw new FileNotFoundException(
"Entry log directory does not exist");
}
long lastLogId = getLastLogId(dir);
if (lastLogId > logId) {
logId = lastLogId;
}
}
initialize();
}
void addListener(EntryLogListener listener) {
if (null != listener) {
listeners.add(listener);
}
}
/**
* Maps entry log files to open channels.
*/
private ConcurrentHashMap channels = new ConcurrentHashMap();
synchronized long getCurrentLogId() {
return logId;
}
protected void initialize() throws IOException {
// Register listener for disk full notifications.
ledgerDirsManager.addLedgerDirsListener(getLedgerDirsListener());
// create a new log to write
createNewLog();
}
private LedgerDirsListener getLedgerDirsListener() {
return new LedgerDirsListener() {
@Override
public void diskFull(File disk) {
// If the current entry log disk is full, then create new entry
// log.
if (currentDir != null && currentDir.equals(disk)) {
shouldCreateNewEntryLog.set(true);
}
}
@Override
public void diskFailed(File disk) {
// Nothing to handle here. Will be handled in Bookie
}
@Override
public void allDisksFull() {
// Nothing to handle here. Will be handled in Bookie
}
@Override
public void fatalError() {
// Nothing to handle here. Will be handled in Bookie
}
};
}
/**
* Creates a new log file
*/
void createNewLog() throws IOException {
if (logChannel != null) {
logChannel.flush(true);
}
// It would better not to overwrite existing entry log files
String logFileName = null;
do {
logFileName = Long.toHexString(++logId) + ".log";
for (File dir : ledgerDirsManager.getAllLedgerDirs()) {
File newLogFile = new File(dir, logFileName);
if (newLogFile.exists()) {
LOG.warn("Found existed entry log " + newLogFile
+ " when trying to create it as a new log.");
logFileName = null;
break;
}
}
} while (logFileName == null);
// Update last log id first
currentDir = ledgerDirsManager.pickRandomWritableDir();
setLastLogId(currentDir, logId);
File newLogFile = new File(currentDir, logFileName);
logChannel = new BufferedChannel(new RandomAccessFile(newLogFile, "rw").getChannel(), 64*1024);
logChannel.write((ByteBuffer) LOGFILE_HEADER.clear());
channels.put(logId, logChannel);
}
/**
* Remove entry log.
*
* @param entryLogId
* Entry Log File Id
*/
protected boolean removeEntryLog(long entryLogId) {
BufferedChannel bc = channels.remove(entryLogId);
if (null != bc) {
// close its underlying file channel, so it could be deleted really
try {
bc.getFileChannel().close();
} catch (IOException ie) {
LOG.warn("Exception while closing garbage collected entryLog file : ", ie);
}
}
File entryLogFile;
try {
entryLogFile = findFile(entryLogId);
} catch (FileNotFoundException e) {
LOG.error("Trying to delete an entryLog file that could not be found: "
+ entryLogId + ".log");
return false;
}
if (!entryLogFile.delete()) {
LOG.warn("Could not delete entry log file {}", entryLogFile);
}
return true;
}
/**
* writes the given id to the "lastId" file in the given directory.
*/
private void setLastLogId(File dir, long logId) throws IOException {
FileOutputStream fos;
fos = new FileOutputStream(new File(dir, "lastId"));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos));
try {
bw.write(Long.toHexString(logId) + "\n");
bw.flush();
} finally {
try {
bw.close();
} catch (IOException e) {
}
}
}
private long getLastLogId(File dir) {
long id = readLastLogId(dir);
// read success
if (id > 0) {
return id;
}
// read failed, scan the ledger directories to find biggest log id
File[] logFiles = dir.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return file.getName().endsWith(".log");
}
});
List logs = new ArrayList();
for (File lf : logFiles) {
String idString = lf.getName().split("\\.")[0];
try {
long lid = Long.parseLong(idString, 16);
logs.add(lid);
} catch (NumberFormatException nfe) {
}
}
// no log file found in this directory
if (0 == logs.size()) {
return -1;
}
// order the collections
Collections.sort(logs);
return logs.get(logs.size() - 1);
}
/**
* reads id from the "lastId" file in the given directory.
*/
private long readLastLogId(File f) {
FileInputStream fis;
try {
fis = new FileInputStream(new File(f, "lastId"));
} catch (FileNotFoundException e) {
return -1;
}
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
try {
String lastIdString = br.readLine();
return Long.parseLong(lastIdString, 16);
} catch (IOException e) {
return -1;
} catch(NumberFormatException e) {
return -1;
} finally {
try {
br.close();
} catch (IOException e) {
}
}
}
synchronized void flush() throws IOException {
if (logChannel != null) {
logChannel.flush(true);
}
somethingWritten = false;
for (EntryLogListener listener: listeners) {
listener.onEntryLogFlushed();
}
}
boolean isFlushRequired() {
return somethingWritten;
}
synchronized long addEntry(long ledger, ByteBuffer entry) throws IOException {
// Create new log if logSizeLimit reached or current disk is full
boolean createNewLog = shouldCreateNewEntryLog.get();
if (createNewLog
|| (logChannel.position() + entry.remaining() + 4 > logSizeLimit)) {
createNewLog();
// Reset the flag
if (createNewLog) {
shouldCreateNewEntryLog.set(false);
}
}
ByteBuffer buff = ByteBuffer.allocate(4);
buff.putInt(entry.remaining());
buff.flip();
logChannel.write(buff);
long pos = logChannel.position();
logChannel.write(entry);
//logChannel.flush(false);
somethingWritten = true;
return (logId << 32L) | pos;
}
byte[] readEntry(long ledgerId, long entryId, long location) throws IOException, Bookie.NoEntryException {
long entryLogId = location >> 32L;
long pos = location & 0xffffffffL;
ByteBuffer sizeBuff = ByteBuffer.allocate(4);
pos -= 4; // we want to get the ledgerId and length to check
BufferedChannel fc;
try {
fc = getChannelForLogId(entryLogId);
} catch (FileNotFoundException e) {
FileNotFoundException newe = new FileNotFoundException(e.getMessage() + " for " + ledgerId + " with location " + location);
newe.setStackTrace(e.getStackTrace());
throw newe;
}
if (fc.read(sizeBuff, pos) != sizeBuff.capacity()) {
throw new Bookie.NoEntryException("Short read from entrylog " + entryLogId,
ledgerId, entryId);
}
pos += 4;
sizeBuff.flip();
int entrySize = sizeBuff.getInt();
// entrySize does not include the ledgerId
if (entrySize > MB) {
LOG.error("Sanity check failed for entry size of " + entrySize + " at location " + pos + " in " + entryLogId);
}
if (entrySize < MIN_SANE_ENTRY_SIZE) {
LOG.error("Read invalid entry length {}", entrySize);
throw new IOException("Invalid entry length " + entrySize);
}
byte data[] = new byte[entrySize];
ByteBuffer buff = ByteBuffer.wrap(data);
int rc = fc.read(buff, pos);
if ( rc != data.length) {
// Note that throwing NoEntryException here instead of IOException is not
// without risk. If all bookies in a quorum throw this same exception
// the client will assume that it has reached the end of the ledger.
// However, this may not be the case, as a very specific error condition
// could have occurred, where the length of the entry was corrupted on all
// replicas. However, the chance of this happening is very very low, so
// returning NoEntryException is mostly safe.
throw new Bookie.NoEntryException("Short read for " + ledgerId + "@"
+ entryId + " in " + entryLogId + "@"
+ pos + "("+rc+"!="+data.length+")", ledgerId, entryId);
}
buff.flip();
long thisLedgerId = buff.getLong();
if (thisLedgerId != ledgerId) {
throw new IOException("problem found in " + entryLogId + "@" + entryId + " at position + " + pos + " entry belongs to " + thisLedgerId + " not " + ledgerId);
}
long thisEntryId = buff.getLong();
if (thisEntryId != entryId) {
throw new IOException("problem found in " + entryLogId + "@" + entryId + " at position + " + pos + " entry is " + thisEntryId + " not " + entryId);
}
return data;
}
private BufferedChannel getChannelForLogId(long entryLogId) throws IOException {
BufferedChannel fc = channels.get(entryLogId);
if (fc != null) {
return fc;
}
File file = findFile(entryLogId);
// get channel is used to open an existing entry log file
// it would be better to open using read mode
FileChannel newFc = new RandomAccessFile(file, "r").getChannel();
// If the file already exists before creating a BufferedChannel layer above it,
// set the FileChannel's position to the end so the write buffer knows where to start.
newFc.position(newFc.size());
fc = new BufferedChannel(newFc, 8192);
BufferedChannel oldfc = channels.putIfAbsent(entryLogId, fc);
if (oldfc != null) {
newFc.close();
return oldfc;
} else {
return fc;
}
}
/**
* Whether the log file exists or not.
*/
boolean logExists(long logId) {
for (File d : ledgerDirsManager.getAllLedgerDirs()) {
File f = new File(d, Long.toHexString(logId) + ".log");
if (f.exists()) {
return true;
}
}
return false;
}
private File findFile(long logId) throws FileNotFoundException {
for (File d : ledgerDirsManager.getAllLedgerDirs()) {
File f = new File(d, Long.toHexString(logId)+".log");
if (f.exists()) {
return f;
}
}
throw new FileNotFoundException("No file for log " + Long.toHexString(logId));
}
/**
* Scan entry log
*
* @param entryLogId
* Entry Log Id
* @param scanner
* Entry Log Scanner
* @throws IOException
*/
protected void scanEntryLog(long entryLogId, EntryLogScanner scanner) throws IOException {
ByteBuffer sizeBuff = ByteBuffer.allocate(4);
ByteBuffer lidBuff = ByteBuffer.allocate(8);
BufferedChannel bc;
// Get the BufferedChannel for the current entry log file
try {
bc = getChannelForLogId(entryLogId);
} catch (IOException e) {
LOG.warn("Failed to get channel to scan entry log: " + entryLogId + ".log");
throw e;
}
// Start the read position in the current entry log file to be after
// the header where all of the ledger entries are.
long pos = LOGFILE_HEADER_SIZE;
// Read through the entry log file and extract the ledger ID's.
while (true) {
// Check if we've finished reading the entry log file.
if (pos >= bc.size()) {
break;
}
if (bc.read(sizeBuff, pos) != sizeBuff.capacity()) {
throw new IOException("Short read for entry size from entrylog " + entryLogId);
}
long offset = pos;
pos += 4;
sizeBuff.flip();
int entrySize = sizeBuff.getInt();
if (entrySize > MB) {
LOG.warn("Found large size entry of " + entrySize + " at location " + pos + " in "
+ entryLogId);
}
sizeBuff.clear();
// try to read ledger id first
if (bc.read(lidBuff, pos) != lidBuff.capacity()) {
throw new IOException("Short read for ledger id from entrylog " + entryLogId);
}
lidBuff.flip();
long lid = lidBuff.getLong();
lidBuff.clear();
if (!scanner.accept(lid)) {
// skip this entry
pos += entrySize;
continue;
}
// read the entry
byte data[] = new byte[entrySize];
ByteBuffer buff = ByteBuffer.wrap(data);
int rc = bc.read(buff, pos);
if (rc != data.length) {
throw new IOException("Short read for ledger entry from entryLog " + entryLogId
+ "@" + pos + "(" + rc + "!=" + data.length + ")");
}
buff.flip();
// process the entry
scanner.process(lid, offset, buff);
// Advance position to the next entry
pos += entrySize;
}
}
/**
* Shutdown method to gracefully stop entry logger.
*/
public void shutdown() {
// since logChannel is buffered channel, do flush when shutting down
try {
flush();
for (Entry channelEntry : channels
.entrySet()) {
channelEntry.getValue().getFileChannel().close();
}
} catch (IOException ie) {
// we have no idea how to avoid io exception during shutting down, so just ignore it
LOG.error("Error flush entry log during shutting down, which may cause entry log corrupted.", ie);
} finally {
for (Entry channelEntry : channels
.entrySet()) {
FileChannel fileChannel = channelEntry.getValue()
.getFileChannel();
if (fileChannel.isOpen()) {
IOUtils.close(LOG, fileChannel);
}
}
}
}
}
bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/ExitCode.java 0000664 0000000 0000000 00000002634 12445073612 0033341 0 ustar 00root root 0000000 0000000 /*
*
* 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.bookie;
/**
* Exit code used to exit bookie server
*/
public class ExitCode {
// normal quit
public final static int OK = 0;
// invalid configuration
public final static int INVALID_CONF = 1;
// exception running bookie server
public final static int SERVER_EXCEPTION = 2;
// zookeeper is expired
public final static int ZK_EXPIRED = 3;
// register bookie on zookeeper failed
public final static int ZK_REG_FAIL = 4;
// exception running bookie
public final static int BOOKIE_EXCEPTION = 5;
}
bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/FileInfo.java 0000664 0000000 0000000 00000027607 12445073612 0033337 0 ustar 00root root 0000000 0000000 /*
*
* 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.bookie;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.BufferUnderflowException;
import java.nio.channels.FileChannel;
import com.google.common.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is the file handle for a ledger's index file that maps entry ids to location.
* It is used by LedgerCache.
*
*
* Ledger index file is made of a header and several fixed-length index pages, which records the offsets of data stored in entry loggers
*
<header><index pages>
* Header is formated as below:
* <magic bytes><len of master key><master key>
*
* - magic bytes: 4 bytes, 'BKLE', version: 4 bytes
*
- len of master key: indicates length of master key. -1 means no master key stored in header.
*
- master key: master key
*
- state: bit map to indicate the state, 32 bits.
*
* Index page is a fixed-length page, which contains serveral entries which point to the offsets of data stored in entry loggers.
*
*/
class FileInfo {
static Logger LOG = LoggerFactory.getLogger(FileInfo.class);
static final int NO_MASTER_KEY = -1;
static final int STATE_FENCED_BIT = 0x1;
private FileChannel fc;
private File lf;
byte[] masterKey;
/**
* The fingerprint of a ledger index file
*/
static final public int signature = ByteBuffer.wrap("BKLE".getBytes()).getInt();
static final public int headerVersion = 0;
static final long START_OF_DATA = 1024;
private long size;
private int useCount;
private boolean isClosed;
private long sizeSinceLastwrite;
// bit map for states of the ledger.
private int stateBits;
private boolean needFlushHeader = false;
// file access mode
protected String mode;
public FileInfo(File lf, byte[] masterKey) throws IOException {
this.lf = lf;
this.masterKey = masterKey;
mode = "rw";
}
public File getLf() {
return lf;
}
public long getSizeSinceLastwrite() {
return sizeSinceLastwrite;
}
synchronized public void readHeader() throws IOException {
if (lf.exists()) {
if (fc != null) {
return;
}
fc = new RandomAccessFile(lf, mode).getChannel();
size = fc.size();
sizeSinceLastwrite = size;
// avoid hang on reading partial index
ByteBuffer bb = ByteBuffer.allocate((int)(Math.min(size, START_OF_DATA)));
while(bb.hasRemaining()) {
fc.read(bb);
}
bb.flip();
if (bb.getInt() != signature) {
throw new IOException("Missing ledger signature");
}
int version = bb.getInt();
if (version != headerVersion) {
throw new IOException("Incompatible ledger version " + version);
}
int length = bb.getInt();
if (length < 0) {
throw new IOException("Length " + length + " is invalid");
} else if (length > bb.remaining()) {
throw new BufferUnderflowException();
}
masterKey = new byte[length];
bb.get(masterKey);
stateBits = bb.getInt();
needFlushHeader = false;
} else {
throw new IOException("Ledger index file does not exist");
}
}
synchronized void checkOpen(boolean create) throws IOException {
if (fc != null) {
return;
}
boolean exists = lf.exists();
if (masterKey == null && !exists) {
throw new IOException(lf + " not found");
}
if (!exists) {
if (create) {
// delayed the creation of parents directories
checkParents(lf);
fc = new RandomAccessFile(lf, mode).getChannel();
size = fc.size();
if (size == 0) {
writeHeader();
}
}
} else {
try {
readHeader();
} catch (BufferUnderflowException buf) {
LOG.warn("Exception when reading header of {} : {}", lf, buf);
if (null != masterKey) {
LOG.warn("Attempting to write header of {} again.", lf);
writeHeader();
} else {
throw new IOException("Error reading header " + lf);
}
}
}
}
private void writeHeader() throws IOException {
ByteBuffer bb = ByteBuffer.allocate((int)START_OF_DATA);
bb.putInt(signature);
bb.putInt(headerVersion);
bb.putInt(masterKey.length);
bb.put(masterKey);
bb.putInt(stateBits);
bb.rewind();
fc.position(0);
fc.write(bb);
}
synchronized public boolean isFenced() throws IOException {
checkOpen(false);
return (stateBits & STATE_FENCED_BIT) == STATE_FENCED_BIT;
}
/**
* @return true if set fence succeed, otherwise false when
* it already fenced or failed to set fenced.
*/
synchronized public boolean setFenced() throws IOException {
checkOpen(false);
LOG.debug("Try to set fenced state in file info {} : state bits {}.", lf, stateBits);
if ((stateBits & STATE_FENCED_BIT) != STATE_FENCED_BIT) {
// not fenced yet
stateBits |= STATE_FENCED_BIT;
needFlushHeader = true;
return true;
} else {
return false;
}
}
// flush the header when header is changed
synchronized public void flushHeader() throws IOException {
if (needFlushHeader) {
checkOpen(true);
writeHeader();
needFlushHeader = false;
}
}
synchronized public long size() throws IOException {
checkOpen(false);
long rc = size-START_OF_DATA;
if (rc < 0) {
rc = 0;
}
return rc;
}
synchronized public int read(ByteBuffer bb, long position) throws IOException {
return readAbsolute(bb, position + START_OF_DATA);
}
private int readAbsolute(ByteBuffer bb, long start) throws IOException {
checkOpen(false);
if (fc == null) {
return 0;
}
int total = 0;
while(bb.remaining() > 0) {
int rc = fc.read(bb, start);
if (rc <= 0) {
throw new IOException("Short read");
}
total += rc;
// should move read position
start += rc;
}
return total;
}
/**
* Close a file info
*
* @param force
* if set to true, the index is forced to create before closed,
* if set to false, the index is not forced to create.
*/
synchronized public void close(boolean force) throws IOException {
isClosed = true;
checkOpen(force);
// Any time when we force close a file, we should try to flush header. otherwise, we might lose fence bit.
if (force) {
flushHeader();
}
if (useCount == 0 && fc != null) {
fc.close();
}
}
synchronized public long write(ByteBuffer[] buffs, long position) throws IOException {
checkOpen(true);
long total = 0;
try {
fc.position(position+START_OF_DATA);
while(buffs[buffs.length-1].remaining() > 0) {
long rc = fc.write(buffs);
if (rc <= 0) {
throw new IOException("Short write");
}
total += rc;
}
} finally {
fc.force(true);
long newsize = position+START_OF_DATA+total;
if (newsize > size) {
size = newsize;
}
}
sizeSinceLastwrite = fc.size();
return total;
}
/**
* Copies current file contents upto specified size to the target file and
* deletes the current file. If size not known then pass size as
* Long.MAX_VALUE to copy complete file.
*/
public synchronized void moveToNewLocation(File newFile, long size) throws IOException {
checkOpen(false);
if (fc != null) {
if (size > fc.size()) {
size = fc.size();
}
File rlocFile = new File(newFile.getParentFile(), newFile.getName() + LedgerCacheImpl.RLOC);
if (!rlocFile.exists()) {
checkParents(rlocFile);
if (!rlocFile.createNewFile()) {
throw new IOException("Creating new cache index file " + rlocFile + " failed ");
}
}
// copy contents from old.idx to new.idx.rloc
FileChannel newFc = new RandomAccessFile(rlocFile, "rw").getChannel();
try {
long written = 0;
while (written < size) {
long count = fc.transferTo(written, size, newFc);
if (count <= 0) {
throw new IOException("Copying to new location " + rlocFile + " failed");
}
written += count;
}
if (written <= 0 && size > 0) {
throw new IOException("Copying to new location " + rlocFile + " failed");
}
} finally {
newFc.force(true);
newFc.close();
}
// delete old.idx
fc.close();
if (!delete()) {
LOG.error("Failed to delete the previous index file " + lf);
throw new IOException("Failed to delete the previous index file " + lf);
}
// rename new.idx.rloc to new.idx
if (!rlocFile.renameTo(newFile)) {
LOG.error("Failed to rename " + rlocFile + " to " + newFile);
throw new IOException("Failed to rename " + rlocFile + " to " + newFile);
}
fc = new RandomAccessFile(newFile, mode).getChannel();
}
lf = newFile;
}
synchronized public byte[] getMasterKey() throws IOException {
checkOpen(false);
return masterKey;
}
synchronized public void use() {
useCount++;
}
@VisibleForTesting
synchronized int getUseCount() {
return useCount;
}
synchronized public void release() {
useCount--;
if (isClosed && useCount == 0 && fc != null) {
try {
fc.close();
} catch (IOException e) {
LOG.error("Error closing file channel", e);
}
}
}
public boolean delete() {
return lf.delete();
}
static final private void checkParents(File f) throws IOException {
File parent = f.getParentFile();
if (parent.exists()) {
return;
}
if (parent.mkdirs() == false) {
throw new IOException("Counldn't mkdirs for " + parent);
}
}
}
FileSystemUpgrade.java 0000664 0000000 0000000 00000035724 12445073612 0035160 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /**
*
* 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.bookie;
import org.apache.bookkeeper.util.BookKeeperConstants;
import org.apache.bookkeeper.util.HardLink;
import org.apache.commons.io.FileUtils;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.NoSuchElementException;
import java.net.MalformedURLException;
import org.apache.bookkeeper.conf.ServerConfiguration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.KeeperException;
/**
* Application for upgrading the bookkeeper filesystem
* between versions
*/
public class FileSystemUpgrade {
static Logger LOG = LoggerFactory.getLogger(FileSystemUpgrade.class);
static FilenameFilter BOOKIE_FILES_FILTER = new FilenameFilter() {
private boolean containsIndexFiles(File dir, String name) {
if (name.endsWith(".idx")) {
return true;
}
try {
Long.parseLong(name, 16);
File d = new File(dir, name);
if (d.isDirectory()) {
String[] files = d.list();
for (String f : files) {
if (containsIndexFiles(d, f)) {
return true;
}
}
}
} catch (NumberFormatException nfe) {
return false;
}
return false;
}
public boolean accept(File dir, String name) {
if (name.endsWith(".txn") || name.endsWith(".log")
|| name.equals("lastId") || name.equals("lastMark")) {
return true;
}
if (containsIndexFiles(dir, name)) {
return true;
}
return false;
}
};
private static List getAllDirectories(ServerConfiguration conf) {
List dirs = new ArrayList();
dirs.add(conf.getJournalDir());
for (File d: conf.getLedgerDirs()) {
dirs.add(d);
}
return dirs;
}
private static int detectPreviousVersion(File directory) throws IOException {
String[] files = directory.list(BOOKIE_FILES_FILTER);
File v2versionFile = new File(directory,
BookKeeperConstants.VERSION_FILENAME);
if (files.length == 0 && !v2versionFile.exists()) { // no old data, so we're ok
return Cookie.CURRENT_COOKIE_LAYOUT_VERSION;
}
if (!v2versionFile.exists()) {
return 1;
}
Scanner s = new Scanner(v2versionFile);
try {
return s.nextInt();
} catch (NoSuchElementException nse) {
LOG.error("Couldn't parse version file " + v2versionFile , nse);
throw new IOException("Couldn't parse version file", nse);
} catch (IllegalStateException ise) {
LOG.error("Error reading file " + v2versionFile, ise);
throw new IOException("Error reading version file", ise);
} finally {
s.close();
}
}
private static ZooKeeper newZookeeper(final ServerConfiguration conf)
throws BookieException.UpgradeException {
try {
final CountDownLatch latch = new CountDownLatch(1);
ZooKeeper zk = new ZooKeeper(conf.getZkServers(), conf.getZkTimeout(),
new Watcher() {
@Override
public void process(WatchedEvent event) {
// handle session disconnects and expires
if (event.getState().equals(Watcher.Event.KeeperState.SyncConnected)) {
latch.countDown();
}
}
});
if (!latch.await(conf.getZkTimeout()*2, TimeUnit.MILLISECONDS)) {
zk.close();
throw new BookieException.UpgradeException("Couldn't connect to zookeeper");
}
return zk;
} catch (InterruptedException ie) {
throw new BookieException.UpgradeException(ie);
} catch (IOException ioe) {
throw new BookieException.UpgradeException(ioe);
}
}
private static void linkIndexDirectories(File srcPath, File targetPath) throws IOException {
String[] files = srcPath.list();
for (String f : files) {
if (f.endsWith(".idx")) { // this is an index dir, create the links
if (!targetPath.mkdirs()) {
throw new IOException("Could not create target path ["+targetPath+"]");
}
HardLink.createHardLinkMult(srcPath, files, targetPath);
return;
}
File newSrcPath = new File(srcPath, f);
if (newSrcPath.isDirectory()) {
try {
Long.parseLong(f, 16);
linkIndexDirectories(newSrcPath, new File(targetPath, f));
} catch (NumberFormatException nfe) {
// filename does not parse to a hex Long, so
// it will not contain idx files. Ignoring
}
}
}
}
public static void upgrade(ServerConfiguration conf)
throws BookieException.UpgradeException, InterruptedException {
LOG.info("Upgrading...");
ZooKeeper zk = newZookeeper(conf);
try {
Map deferredMoves = new HashMap();
Cookie c = Cookie.generateCookie(conf);
for (File d : getAllDirectories(conf)) {
LOG.info("Upgrading {}", d);
int version = detectPreviousVersion(d);
if (version == Cookie.CURRENT_COOKIE_LAYOUT_VERSION) {
LOG.info("Directory is current, no need to upgrade");
continue;
}
try {
File curDir = new File(d, BookKeeperConstants.CURRENT_DIR);
File tmpDir = new File(d, "upgradeTmp." + System.nanoTime());
deferredMoves.put(curDir, tmpDir);
if (!tmpDir.mkdirs()) {
throw new BookieException.UpgradeException("Could not create temporary directory " + tmpDir);
}
c.writeToDirectory(tmpDir);
String[] files = d.list(new FilenameFilter() {
public boolean accept(File dir, String name) {
return BOOKIE_FILES_FILTER.accept(dir, name)
&& !(new File(dir, name).isDirectory());
}
});
HardLink.createHardLinkMult(d, files, tmpDir);
linkIndexDirectories(d, tmpDir);
} catch (IOException ioe) {
LOG.error("Error upgrading {}", d);
throw new BookieException.UpgradeException(ioe);
}
}
for (Map.Entry e : deferredMoves.entrySet()) {
try {
FileUtils.moveDirectory(e.getValue(), e.getKey());
} catch (IOException ioe) {
String err = String.format("Error moving upgraded directories into place %s -> %s ",
e.getValue(), e.getKey());
LOG.error(err, ioe);
throw new BookieException.UpgradeException(ioe);
}
}
if (deferredMoves.isEmpty()) {
return;
}
try {
c.writeToZooKeeper(zk, conf);
} catch (KeeperException ke) {
LOG.error("Error writing cookie to zookeeper");
throw new BookieException.UpgradeException(ke);
}
} catch (IOException ioe) {
throw new BookieException.UpgradeException(ioe);
} finally {
zk.close();
}
LOG.info("Done");
}
public static void finalizeUpgrade(ServerConfiguration conf)
throws BookieException.UpgradeException, InterruptedException {
LOG.info("Finalizing upgrade...");
// verify that upgrade is correct
for (File d : getAllDirectories(conf)) {
LOG.info("Finalizing {}", d);
try {
int version = detectPreviousVersion(d);
if (version < 3) {
if (version == 2) {
File v2versionFile = new File(d,
BookKeeperConstants.VERSION_FILENAME);
if (!v2versionFile.delete()) {
LOG.warn("Could not delete old version file {}", v2versionFile);
}
}
File[] files = d.listFiles(BOOKIE_FILES_FILTER);
for (File f : files) {
if (f.isDirectory()) {
FileUtils.deleteDirectory(f);
} else{
if (!f.delete()) {
LOG.warn("Could not delete {}", f);
}
}
}
}
} catch (IOException ioe) {
LOG.error("Error finalizing {}", d);
throw new BookieException.UpgradeException(ioe);
}
}
// noop at the moment
LOG.info("Done");
}
public static void rollback(ServerConfiguration conf)
throws BookieException.UpgradeException, InterruptedException {
LOG.info("Rolling back upgrade...");
ZooKeeper zk = newZookeeper(conf);
try {
for (File d : getAllDirectories(conf)) {
LOG.info("Rolling back {}", d);
try {
// ensure there is a previous version before rollback
int version = detectPreviousVersion(d);
if (version <= Cookie.CURRENT_COOKIE_LAYOUT_VERSION) {
File curDir = new File(d,
BookKeeperConstants.CURRENT_DIR);
FileUtils.deleteDirectory(curDir);
} else {
throw new BookieException.UpgradeException(
"Cannot rollback as previous data does not exist");
}
} catch (IOException ioe) {
LOG.error("Error rolling back {}", d);
throw new BookieException.UpgradeException(ioe);
}
}
try {
Cookie c = Cookie.readFromZooKeeper(zk, conf);
c.deleteFromZooKeeper(zk, conf);
} catch (KeeperException ke) {
LOG.error("Error deleting cookie from ZooKeeper");
throw new BookieException.UpgradeException(ke);
} catch (IOException ioe) {
LOG.error("I/O Error deleting cookie from ZooKeeper");
throw new BookieException.UpgradeException(ioe);
}
} finally {
zk.close();
}
LOG.info("Done");
}
private static void printHelp(Options opts) {
HelpFormatter hf = new HelpFormatter();
hf.printHelp("FileSystemUpgrade [options]", opts);
}
public static void main(String[] args) throws Exception {
org.apache.log4j.Logger root = org.apache.log4j.Logger.getRootLogger();
root.addAppender(new org.apache.log4j.ConsoleAppender(
new org.apache.log4j.PatternLayout("%-5p [%t]: %m%n")));
root.setLevel(org.apache.log4j.Level.ERROR);
org.apache.log4j.Logger.getLogger(FileSystemUpgrade.class).setLevel(
org.apache.log4j.Level.INFO);
final Options opts = new Options();
opts.addOption("c", "conf", true, "Configuration for Bookie");
opts.addOption("u", "upgrade", false, "Upgrade bookie directories");
opts.addOption("f", "finalize", false, "Finalize upgrade");
opts.addOption("r", "rollback", false, "Rollback upgrade");
opts.addOption("h", "help", false, "Print help message");
BasicParser parser = new BasicParser();
CommandLine cmdLine = parser.parse(opts, args);
if (cmdLine.hasOption("h")) {
printHelp(opts);
return;
}
if (!cmdLine.hasOption("c")) {
String err = "Cannot upgrade without configuration";
LOG.error(err);
printHelp(opts);
throw new IllegalArgumentException(err);
}
String confFile = cmdLine.getOptionValue("c");
ServerConfiguration conf = new ServerConfiguration();
try {
conf.loadConf(new File(confFile).toURI().toURL());
} catch (MalformedURLException mue) {
LOG.error("Could not open configuration file " + confFile, mue);
throw new IllegalArgumentException();
} catch (ConfigurationException ce) {
LOG.error("Invalid configuration file " + confFile, ce);
throw new IllegalArgumentException();
}
if (cmdLine.hasOption("u")) {
upgrade(conf);
} else if (cmdLine.hasOption("r")) {
rollback(conf);
} else if (cmdLine.hasOption("f")) {
finalizeUpgrade(conf);
} else {
String err = "Must specify -upgrade, -finalize or -rollback";
LOG.error(err);
printHelp(opts);
throw new IllegalArgumentException(err);
}
}
}
GarbageCollector.java 0000664 0000000 0000000 00000003024 12445073612 0034747 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /**
*
* 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.bookie;
/**
* This is the garbage collector interface, garbage collector implementers
* need to extends this class to remove the deleted ledgers.
*/
public interface GarbageCollector {
/**
* Do the garbage collector work
*
* @param garbageCleaner
* cleaner used to clean selected garbages
*/
public abstract void gc(GarbageCleaner garbageCleaner);
/**
* A interface used to define customised garbage cleaner
*/
public interface GarbageCleaner {
/**
* Clean a specific ledger
*
* @param ledgerId
* Ledger ID to be cleaned
*/
public void clean(final long ledgerId) ;
}
}
GarbageCollectorThread.java 0000664 0000000 0000000 00000055130 12445073612 0036104 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /**
*
* 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.bookie;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import com.google.common.util.concurrent.RateLimiter;
import org.apache.bookkeeper.bookie.EntryLogger.EntryLogScanner;
import org.apache.bookkeeper.bookie.GarbageCollector.GarbageCleaner;
import org.apache.bookkeeper.conf.ServerConfiguration;
import org.apache.bookkeeper.meta.LedgerManager;
import org.apache.bookkeeper.util.MathUtils;
import org.apache.bookkeeper.util.SnapshotMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is the garbage collector thread that runs in the background to
* remove any entry log files that no longer contains any active ledger.
*/
public class GarbageCollectorThread extends Thread {
private static final Logger LOG = LoggerFactory.getLogger(GarbageCollectorThread.class);
private static final int SECOND = 1000;
// Maps entry log files to the set of ledgers that comprise the file and the size usage per ledger
private Map entryLogMetaMap = new ConcurrentHashMap();
// This is how often we want to run the Garbage Collector Thread (in milliseconds).
final long gcWaitTime;
// Compaction parameters
boolean enableMinorCompaction = false;
final double minorCompactionThreshold;
final long minorCompactionInterval;
boolean enableMajorCompaction = false;
final double majorCompactionThreshold;
final long majorCompactionInterval;
long lastMinorCompactionTime;
long lastMajorCompactionTime;
final int maxOutstandingRequests;
final int compactionRate;
final CompactionScannerFactory scannerFactory;
// Entry Logger Handle
final EntryLogger entryLogger;
// Ledger Cache Handle
final LedgerCache ledgerCache;
final SnapshotMap activeLedgers;
// flag to ensure gc thread will not be interrupted during compaction
// to reduce the risk getting entry log corrupted
final AtomicBoolean compacting = new AtomicBoolean(false);
volatile boolean running = true;
// track the last scanned successfully log id
long scannedLogId = 0;
final GarbageCollector garbageCollector;
final GarbageCleaner garbageCleaner;
private static class Offset {
final long ledger;
final long entry;
final long offset;
Offset(long ledger, long entry, long offset) {
this.ledger = ledger;
this.entry = entry;
this.offset = offset;
}
}
/**
* A scanner wrapper to check whether a ledger is alive in an entry log file
*/
class CompactionScannerFactory implements EntryLogger.EntryLogListener {
List offsets = new ArrayList();
EntryLogScanner newScanner(final EntryLogMetadata meta) {
final RateLimiter rateLimiter = RateLimiter.create(compactionRate);
return new EntryLogScanner() {
@Override
public boolean accept(long ledgerId) {
return meta.containsLedger(ledgerId);
}
@Override
public void process(final long ledgerId, long offset, ByteBuffer entry)
throws IOException {
rateLimiter.acquire();
synchronized (CompactionScannerFactory.this) {
if (offsets.size() > maxOutstandingRequests) {
waitEntrylogFlushed();
}
entry.getLong(); // discard ledger id, we already have it
long entryId = entry.getLong();
entry.rewind();
long newoffset = entryLogger.addEntry(ledgerId, entry);
flushed.set(false);
offsets.add(new Offset(ledgerId, entryId, newoffset));
}
}
};
}
AtomicBoolean flushed = new AtomicBoolean(false);
Object flushLock = new Object();
@Override
public void onEntryLogFlushed() {
synchronized (flushLock) {
flushed.set(true);
flushLock.notifyAll();
}
}
synchronized private void waitEntrylogFlushed() throws IOException {
try {
synchronized (flushLock) {
while (!flushed.get()
&& entryLogger.isFlushRequired()
&& running) {
flushLock.wait(1000);
}
if (!flushed.get()
&& entryLogger.isFlushRequired()
&& !running) {
throw new IOException("Shutdown before flushed");
}
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new IOException("Interrupted waiting for flush", ie);
}
for (Offset o : offsets) {
ledgerCache.putEntryOffset(o.ledger, o.entry, o.offset);
}
offsets.clear();
}
synchronized void flush() throws IOException {
waitEntrylogFlushed();
ledgerCache.flushLedger(true);
}
}
/**
* Create a garbage collector thread.
*
* @param conf
* Server Configuration Object.
* @throws IOException
*/
public GarbageCollectorThread(ServerConfiguration conf,
final LedgerCache ledgerCache,
EntryLogger entryLogger,
SnapshotMap activeLedgers,
LedgerManager ledgerManager)
throws IOException {
super("GarbageCollectorThread");
this.ledgerCache = ledgerCache;
this.entryLogger = entryLogger;
this.activeLedgers = activeLedgers;
this.gcWaitTime = conf.getGcWaitTime();
this.maxOutstandingRequests = conf.getCompactionMaxOutstandingRequests();
this.compactionRate = conf.getCompactionRate();
this.scannerFactory = new CompactionScannerFactory();
entryLogger.addListener(this.scannerFactory);
this.garbageCleaner = new GarbageCollector.GarbageCleaner() {
@Override
public void clean(long ledgerId) {
try {
if (LOG.isDebugEnabled()) {
LOG.debug("delete ledger : " + ledgerId);
}
ledgerCache.deleteLedger(ledgerId);
} catch (IOException e) {
LOG.error("Exception when deleting the ledger index file on the Bookie: ", e);
}
}
};
this.garbageCollector = new ScanAndCompareGarbageCollector(ledgerManager, activeLedgers);
// compaction parameters
minorCompactionThreshold = conf.getMinorCompactionThreshold();
minorCompactionInterval = conf.getMinorCompactionInterval() * SECOND;
majorCompactionThreshold = conf.getMajorCompactionThreshold();
majorCompactionInterval = conf.getMajorCompactionInterval() * SECOND;
if (minorCompactionInterval > 0 && minorCompactionThreshold > 0) {
if (minorCompactionThreshold > 1.0f) {
throw new IOException("Invalid minor compaction threshold "
+ minorCompactionThreshold);
}
if (minorCompactionInterval <= gcWaitTime) {
throw new IOException("Too short minor compaction interval : "
+ minorCompactionInterval);
}
enableMinorCompaction = true;
}
if (majorCompactionInterval > 0 && majorCompactionThreshold > 0) {
if (majorCompactionThreshold > 1.0f) {
throw new IOException("Invalid major compaction threshold "
+ majorCompactionThreshold);
}
if (majorCompactionInterval <= gcWaitTime) {
throw new IOException("Too short major compaction interval : "
+ majorCompactionInterval);
}
enableMajorCompaction = true;
}
if (enableMinorCompaction && enableMajorCompaction) {
if (minorCompactionInterval >= majorCompactionInterval ||
minorCompactionThreshold >= majorCompactionThreshold) {
throw new IOException("Invalid minor/major compaction settings : minor ("
+ minorCompactionThreshold + ", " + minorCompactionInterval
+ "), major (" + majorCompactionThreshold + ", "
+ majorCompactionInterval + ")");
}
}
LOG.info("Minor Compaction : enabled=" + enableMinorCompaction + ", threshold="
+ minorCompactionThreshold + ", interval=" + minorCompactionInterval);
LOG.info("Major Compaction : enabled=" + enableMajorCompaction + ", threshold="
+ majorCompactionThreshold + ", interval=" + majorCompactionInterval);
lastMinorCompactionTime = lastMajorCompactionTime = MathUtils.now();
}
@Override
public void run() {
while (running) {
synchronized (this) {
try {
wait(gcWaitTime);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
continue;
}
}
// Extract all of the ledger ID's that comprise all of the entry logs
// (except for the current new one which is still being written to).
entryLogMetaMap = extractMetaFromEntryLogs(entryLogMetaMap);
// gc inactive/deleted ledgers
doGcLedgers();
// gc entry logs
doGcEntryLogs();
long curTime = MathUtils.now();
if (enableMajorCompaction &&
curTime - lastMajorCompactionTime > majorCompactionInterval) {
// enter major compaction
LOG.info("Enter major compaction");
doCompactEntryLogs(majorCompactionThreshold);
lastMajorCompactionTime = MathUtils.now();
// also move minor compaction time
lastMinorCompactionTime = lastMajorCompactionTime;
continue;
}
if (enableMinorCompaction &&
curTime - lastMinorCompactionTime > minorCompactionInterval) {
// enter minor compaction
LOG.info("Enter minor compaction");
doCompactEntryLogs(minorCompactionThreshold);
lastMinorCompactionTime = MathUtils.now();
}
}
}
/**
* Do garbage collection ledger index files
*/
private void doGcLedgers() {
garbageCollector.gc(garbageCleaner);
}
/**
* Garbage collect those entry loggers which are not associated with any active ledgers
*/
private void doGcEntryLogs() {
// Loop through all of the entry logs and remove the non-active ledgers.
for (Long entryLogId : entryLogMetaMap.keySet()) {
EntryLogMetadata meta = entryLogMetaMap.get(entryLogId);
for (Long entryLogLedger : meta.ledgersMap.keySet()) {
// Remove the entry log ledger from the set if it isn't active.
if (!activeLedgers.containsKey(entryLogLedger)) {
meta.removeLedger(entryLogLedger);
}
}
if (meta.isEmpty()) {
// This means the entry log is not associated with any active ledgers anymore.
// We can remove this entry log file now.
LOG.info("Deleting entryLogId " + entryLogId + " as it has no active ledgers!");
removeEntryLog(entryLogId);
}
}
}
/**
* Compact entry logs if necessary.
*
*
* Compaction will be executed from low unused space to high unused space.
* Those entry log files whose remaining size percentage is higher than threshold
* would not be compacted.
*
*/
private void doCompactEntryLogs(double threshold) {
LOG.info("Do compaction to compact those files lower than " + threshold);
// sort the ledger meta by occupied unused space
Comparator sizeComparator = new Comparator() {
@Override
public int compare(EntryLogMetadata m1, EntryLogMetadata m2) {
long unusedSize1 = m1.totalSize - m1.remainingSize;
long unusedSize2 = m2.totalSize - m2.remainingSize;
if (unusedSize1 > unusedSize2) {
return -1;
} else if (unusedSize1 < unusedSize2) {
return 1;
} else {
return 0;
}
}
};
List logsToCompact = new ArrayList();
logsToCompact.addAll(entryLogMetaMap.values());
Collections.sort(logsToCompact, sizeComparator);
List toRemove = new ArrayList();
for (EntryLogMetadata meta : logsToCompact) {
if (meta.getUsage() >= threshold) {
break;
}
LOG.debug("Compacting entry log {} below threshold {}.", meta.entryLogId, threshold);
try {
compactEntryLog(scannerFactory, meta);
toRemove.add(meta.entryLogId);
} catch (LedgerDirsManager.NoWritableLedgerDirException nwlde) {
LOG.warn("No writable ledger directory available, aborting compaction", nwlde);
break;
} catch (IOException ioe) {
// if compact entry log throws IOException, we don't want to remove that
// entry log. however, if some entries from that log have been readded
// to the entry log, and the offset updated, it's ok to flush that
LOG.error("Error compacting entry log. Log won't be deleted", ioe);
}
if (!running) { // if gc thread is not running, stop compaction
return;
}
}
try {
// compaction finished, flush any outstanding offsets
scannerFactory.flush();
} catch (IOException ioe) {
LOG.error("Cannot flush compacted entries, skip removal", ioe);
return;
}
// offsets have been flushed, its now safe to remove the old entrylogs
for (Long l : toRemove) {
removeEntryLog(l);
}
}
/**
* Shutdown the garbage collector thread.
*
* @throws InterruptedException if there is an exception stopping gc thread.
*/
public void shutdown() throws InterruptedException {
this.running = false;
if (compacting.compareAndSet(false, true)) {
// if setting compacting flag succeed, means gcThread is not compacting now
// it is safe to interrupt itself now
this.interrupt();
}
this.join();
}
/**
* Remove entry log.
*
* @param entryLogId
* Entry Log File Id
*/
private void removeEntryLog(long entryLogId) {
// remove entry log file successfully
if (entryLogger.removeEntryLog(entryLogId)) {
entryLogMetaMap.remove(entryLogId);
}
}
/**
* Compact an entry log.
*
* @param entryLogId
* Entry Log File Id
*/
protected void compactEntryLog(CompactionScannerFactory scannerFactory,
EntryLogMetadata entryLogMeta) throws IOException {
// Similar with Sync Thread
// try to mark compacting flag to make sure it would not be interrupted
// by shutdown during compaction. otherwise it will receive
// ClosedByInterruptException which may cause index file & entry logger
// closed and corrupted.
if (!compacting.compareAndSet(false, true)) {
// set compacting flag failed, means compacting is true now
// indicates another thread wants to interrupt gc thread to exit
return;
}
LOG.info("Compacting entry log : {}", entryLogMeta.entryLogId);
try {
entryLogger.scanEntryLog(entryLogMeta.entryLogId,
scannerFactory.newScanner(entryLogMeta));
} finally {
// clear compacting flag
compacting.set(false);
}
}
/**
* Records the total size, remaining size and the set of ledgers that comprise a entry log.
*/
static class EntryLogMetadata {
long entryLogId;
long totalSize;
long remainingSize;
ConcurrentHashMap ledgersMap;
public EntryLogMetadata(long logId) {
this.entryLogId = logId;
totalSize = remainingSize = 0;
ledgersMap = new ConcurrentHashMap();
}
public void addLedgerSize(long ledgerId, long size) {
totalSize += size;
remainingSize += size;
Long ledgerSize = ledgersMap.get(ledgerId);
if (null == ledgerSize) {
ledgerSize = 0L;
}
ledgerSize += size;
ledgersMap.put(ledgerId, ledgerSize);
}
public void removeLedger(long ledgerId) {
Long size = ledgersMap.remove(ledgerId);
if (null == size) {
return;
}
remainingSize -= size;
}
public boolean containsLedger(long ledgerId) {
return ledgersMap.containsKey(ledgerId);
}
public double getUsage() {
if (totalSize == 0L) {
return 0.0f;
}
return (double)remainingSize / totalSize;
}
public boolean isEmpty() {
return ledgersMap.isEmpty();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("{ totalSize = ").append(totalSize).append(", remainingSize = ")
.append(remainingSize).append(", ledgersMap = ").append(ledgersMap).append(" }");
return sb.toString();
}
}
/**
* A scanner used to extract entry log meta from entry log files.
*/
static class ExtractionScanner implements EntryLogScanner {
EntryLogMetadata meta;
public ExtractionScanner(EntryLogMetadata meta) {
this.meta = meta;
}
@Override
public boolean accept(long ledgerId) {
return true;
}
@Override
public void process(long ledgerId, long offset, ByteBuffer entry) {
// add new entry size of a ledger to entry log meta
meta.addLedgerSize(ledgerId, entry.limit() + 4);
}
}
/**
* Method to read in all of the entry logs (those that we haven't done so yet),
* and find the set of ledger ID's that make up each entry log file.
*
* @param entryLogMetaMap
* Existing EntryLogs to Meta
* @throws IOException
*/
protected Map extractMetaFromEntryLogs(Map entryLogMetaMap) {
// Extract it for every entry log except for the current one.
// Entry Log ID's are just a long value that starts at 0 and increments
// by 1 when the log fills up and we roll to a new one.
long curLogId = entryLogger.getCurrentLogId();
boolean hasExceptionWhenScan = false;
for (long entryLogId = scannedLogId; entryLogId < curLogId; entryLogId++) {
// Comb the current entry log file if it has not already been extracted.
if (entryLogMetaMap.containsKey(entryLogId)) {
continue;
}
// check whether log file exists or not
// if it doesn't exist, this log file might have been garbage collected.
if (!entryLogger.logExists(entryLogId)) {
continue;
}
LOG.info("Extracting entry log meta from entryLogId: {}", entryLogId);
try {
// Read through the entry log file and extract the entry log meta
EntryLogMetadata entryLogMeta = extractMetaFromEntryLog(entryLogger, entryLogId);
entryLogMetaMap.put(entryLogId, entryLogMeta);
} catch (IOException e) {
hasExceptionWhenScan = true;
LOG.warn("Premature exception when processing " + entryLogId +
" recovery will take care of the problem", e);
}
// if scan failed on some entry log, we don't move 'scannedLogId' to next id
// if scan succeed, we don't need to scan it again during next gc run,
// we move 'scannedLogId' to next id
if (!hasExceptionWhenScan) {
++scannedLogId;
}
}
return entryLogMetaMap;
}
static EntryLogMetadata extractMetaFromEntryLog(EntryLogger entryLogger, long entryLogId)
throws IOException {
EntryLogMetadata entryLogMeta = new EntryLogMetadata(entryLogId);
ExtractionScanner scanner = new ExtractionScanner(entryLogMeta);
// Read through the entry log file and extract the entry log meta
entryLogger.scanEntryLog(entryLogId, scanner);
LOG.debug("Retrieved entry log meta data entryLogId: {}, meta: {}",
entryLogId, entryLogMeta);
return entryLogMeta;
}
}
HandleFactory.java 0000664 0000000 0000000 00000002160 12445073612 0034273 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /*
*
* 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.bookie;
import java.io.IOException;
interface HandleFactory {
LedgerDescriptor getHandle(long ledgerId, byte[] masterKey)
throws IOException, BookieException;
LedgerDescriptor getReadOnlyHandle(long ledgerId)
throws IOException, Bookie.NoLedgerException;
} HandleFactoryImpl.java 0000664 0000000 0000000 00000004364 12445073612 0035125 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /*
*
* 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.bookie;
import java.io.IOException;
import java.util.HashMap;
class HandleFactoryImpl implements HandleFactory {
HashMap ledgers = new HashMap();
HashMap readOnlyLedgers
= new HashMap();
final LedgerStorage ledgerStorage;
HandleFactoryImpl(LedgerStorage ledgerStorage) {
this.ledgerStorage = ledgerStorage;
}
@Override
public LedgerDescriptor getHandle(long ledgerId, byte[] masterKey)
throws IOException, BookieException {
LedgerDescriptor handle = null;
synchronized (ledgers) {
handle = ledgers.get(ledgerId);
if (handle == null) {
handle = LedgerDescriptor.create(masterKey, ledgerId, ledgerStorage);
ledgers.put(ledgerId, handle);
}
handle.checkAccess(masterKey);
}
return handle;
}
@Override
public LedgerDescriptor getReadOnlyHandle(long ledgerId)
throws IOException, Bookie.NoLedgerException {
LedgerDescriptor handle = null;
synchronized (ledgers) {
handle = readOnlyLedgers.get(ledgerId);
if (handle == null) {
handle = LedgerDescriptor.createReadOnly(ledgerId, ledgerStorage);
readOnlyLedgers.put(ledgerId, handle);
}
}
return handle;
}
} InterleavedLedgerStorage.java 0000664 0000000 0000000 00000013137 12445073612 0036470 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /*
*
* 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.bookie;
import java.nio.ByteBuffer;
import java.io.IOException;
import org.apache.bookkeeper.jmx.BKMBeanInfo;
import org.apache.bookkeeper.conf.ServerConfiguration;
import org.apache.bookkeeper.meta.LedgerManager;
import org.apache.bookkeeper.proto.BookieProtocol;
import org.apache.bookkeeper.util.SnapshotMap;
import org.apache.zookeeper.ZooKeeper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Interleave ledger storage
* This ledger storage implementation stores all entries in a single
* file and maintains an index file for each ledger.
*/
class InterleavedLedgerStorage implements LedgerStorage {
final static Logger LOG = LoggerFactory.getLogger(InterleavedLedgerStorage.class);
EntryLogger entryLogger;
LedgerCache ledgerCache;
// A sorted map to stored all active ledger ids
protected final SnapshotMap activeLedgers;
// This is the thread that garbage collects the entry logs that do not
// contain any active ledgers in them; and compacts the entry logs that
// has lower remaining percentage to reclaim disk space.
final GarbageCollectorThread gcThread;
InterleavedLedgerStorage(ServerConfiguration conf,
LedgerManager ledgerManager, LedgerDirsManager ledgerDirsManager)
throws IOException {
activeLedgers = new SnapshotMap();
entryLogger = new EntryLogger(conf, ledgerDirsManager);
ledgerCache = new LedgerCacheImpl(conf, activeLedgers, ledgerDirsManager);
gcThread = new GarbageCollectorThread(conf, ledgerCache, entryLogger,
activeLedgers, ledgerManager);
}
@Override
public void start() {
gcThread.start();
}
@Override
public void shutdown() throws InterruptedException {
// shut down gc thread, which depends on zookeeper client
// also compaction will write entries again to entry log file
gcThread.shutdown();
entryLogger.shutdown();
try {
ledgerCache.close();
} catch (IOException e) {
LOG.error("Error while closing the ledger cache", e);
}
}
@Override
public boolean setFenced(long ledgerId) throws IOException {
return ledgerCache.setFenced(ledgerId);
}
@Override
public boolean isFenced(long ledgerId) throws IOException {
return ledgerCache.isFenced(ledgerId);
}
@Override
public void setMasterKey(long ledgerId, byte[] masterKey) throws IOException {
ledgerCache.setMasterKey(ledgerId, masterKey);
}
@Override
public byte[] readMasterKey(long ledgerId) throws IOException, BookieException {
return ledgerCache.readMasterKey(ledgerId);
}
@Override
public boolean ledgerExists(long ledgerId) throws IOException {
return ledgerCache.ledgerExists(ledgerId);
}
@Override
synchronized public long addEntry(ByteBuffer entry) throws IOException {
long ledgerId = entry.getLong();
long entryId = entry.getLong();
entry.rewind();
/*
* Log the entry
*/
long pos = entryLogger.addEntry(ledgerId, entry);
/*
* Set offset of entry id to be the current ledger position
*/
ledgerCache.putEntryOffset(ledgerId, entryId, pos);
return entryId;
}
@Override
public ByteBuffer getEntry(long ledgerId, long entryId) throws IOException {
long offset;
/*
* If entryId is BookieProtocol.LAST_ADD_CONFIRMED, then return the last written.
*/
if (entryId == BookieProtocol.LAST_ADD_CONFIRMED) {
entryId = ledgerCache.getLastEntry(ledgerId);
}
offset = ledgerCache.getEntryOffset(ledgerId, entryId);
if (offset == 0) {
throw new Bookie.NoEntryException(ledgerId, entryId);
}
return ByteBuffer.wrap(entryLogger.readEntry(ledgerId, entryId, offset));
}
@Override
public boolean isFlushRequired() {
return entryLogger.isFlushRequired();
}
@Override
public void flush() throws IOException {
if (!isFlushRequired()) {
return;
}
boolean flushFailed = false;
try {
ledgerCache.flushLedger(true);
} catch (IOException ioe) {
LOG.error("Exception flushing Ledger cache", ioe);
flushFailed = true;
}
try {
entryLogger.flush();
} catch (IOException ioe) {
LOG.error("Exception flushing Ledger", ioe);
flushFailed = true;
}
if (flushFailed) {
throw new IOException("Flushing to storage failed, check logs");
}
}
@Override
public BKMBeanInfo getJMXBean() {
return ledgerCache.getJMXBean();
}
}
bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/Journal.java 0000664 0000000 0000000 00000050012 12445073612 0033240 0 ustar 00root root 0000000 0000000 /**
*
* 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.bookie;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import org.apache.bookkeeper.bookie.LedgerDirsManager.NoWritableLedgerDirException;
import org.apache.bookkeeper.conf.ServerConfiguration;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.WriteCallback;
import org.apache.bookkeeper.util.IOUtils;
import org.apache.bookkeeper.util.MathUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Provide journal related management.
*/
class Journal extends Thread {
static Logger LOG = LoggerFactory.getLogger(Journal.class);
/**
* Filter to pickup journals
*/
private static interface JournalIdFilter {
public boolean accept(long journalId);
}
/**
* List all journal ids by a specified journal id filer
*
* @param journalDir journal dir
* @param filter journal id filter
* @return list of filtered ids
*/
private static List listJournalIds(File journalDir, JournalIdFilter filter) {
File logFiles[] = journalDir.listFiles();
List logs = new ArrayList();
for(File f: logFiles) {
String name = f.getName();
if (!name.endsWith(".txn")) {
continue;
}
String idString = name.split("\\.")[0];
long id = Long.parseLong(idString, 16);
if (filter != null) {
if (filter.accept(id)) {
logs.add(id);
}
} else {
logs.add(id);
}
}
Collections.sort(logs);
return logs;
}
/**
* Last Log Mark
*/
class LastLogMark {
private long txnLogId;
private long txnLogPosition;
private LastLogMark lastMark;
LastLogMark(long logId, long logPosition) {
this.txnLogId = logId;
this.txnLogPosition = logPosition;
}
synchronized void setLastLogMark(long logId, long logPosition) {
txnLogId = logId;
txnLogPosition = logPosition;
}
synchronized void markLog() {
lastMark = new LastLogMark(txnLogId, txnLogPosition);
}
synchronized LastLogMark getLastMark() {
return lastMark;
}
synchronized long getTxnLogId() {
return txnLogId;
}
synchronized long getTxnLogPosition() {
return txnLogPosition;
}
synchronized void rollLog() throws NoWritableLedgerDirException {
byte buff[] = new byte[16];
ByteBuffer bb = ByteBuffer.wrap(buff);
// we should record marked in markLog
// which is safe since records before lastMark have been
// persisted to disk (both index & entry logger)
bb.putLong(lastMark.getTxnLogId());
bb.putLong(lastMark.getTxnLogPosition());
LOG.debug("RollLog to persist last marked log : {}", lastMark);
List writableLedgerDirs = ledgerDirsManager
.getWritableLedgerDirs();
for (File dir : writableLedgerDirs) {
File file = new File(dir, "lastMark");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
fos.write(buff);
fos.getChannel().force(true);
fos.close();
fos = null;
} catch (IOException e) {
LOG.error("Problems writing to " + file, e);
} finally {
// if stream already closed in try block successfully,
// stream might have nullified, in such case below
// call will simply returns
IOUtils.close(LOG, fos);
}
}
}
/**
* Read last mark from lastMark file.
* The last mark should first be max journal log id,
* and then max log position in max journal log.
*/
synchronized void readLog() {
byte buff[] = new byte[16];
ByteBuffer bb = ByteBuffer.wrap(buff);
for(File dir: ledgerDirsManager.getAllLedgerDirs()) {
File file = new File(dir, "lastMark");
try {
FileInputStream fis = new FileInputStream(file);
try {
int bytesRead = fis.read(buff);
if (bytesRead != 16) {
throw new IOException("Couldn't read enough bytes from lastMark."
+ " Wanted " + 16 + ", got " + bytesRead);
}
} finally {
fis.close();
}
bb.clear();
long i = bb.getLong();
long p = bb.getLong();
if (i > txnLogId) {
txnLogId = i;
if(p > txnLogPosition) {
txnLogPosition = p;
}
}
} catch (IOException e) {
LOG.error("Problems reading from " + file + " (this is okay if it is the first time starting this bookie");
}
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("LastMark: logId - ").append(txnLogId)
.append(" , position - ").append(txnLogPosition);
return sb.toString();
}
}
/**
* Filter to return list of journals for rolling
*/
private class JournalRollingFilter implements JournalIdFilter {
@Override
public boolean accept(long journalId) {
if (journalId < lastLogMark.getLastMark().getTxnLogId()) {
return true;
} else {
return false;
}
}
}
/**
* Scanner used to scan a journal
*/
public static interface JournalScanner {
/**
* Process a journal entry.
*
* @param journalVersion
* Journal Version
* @param offset
* File offset of the journal entry
* @param entry
* Journal Entry
* @throws IOException
*/
public void process(int journalVersion, long offset, ByteBuffer entry) throws IOException;
}
/**
* Journal Entry to Record
*/
private static class QueueEntry {
QueueEntry(ByteBuffer entry, long ledgerId, long entryId,
WriteCallback cb, Object ctx) {
this.entry = entry.duplicate();
this.cb = cb;
this.ctx = ctx;
this.ledgerId = ledgerId;
this.entryId = entryId;
}
ByteBuffer entry;
long ledgerId;
long entryId;
WriteCallback cb;
Object ctx;
}
final static long MB = 1024 * 1024L;
// max journal file size
final long maxJournalSize;
// number journal files kept before marked journal
final int maxBackupJournals;
final File journalDirectory;
final ServerConfiguration conf;
private LastLogMark lastLogMark = new LastLogMark(0, 0);
// journal entry queue to commit
LinkedBlockingQueue queue = new LinkedBlockingQueue();
volatile boolean running = true;
private LedgerDirsManager ledgerDirsManager;
public Journal(ServerConfiguration conf, LedgerDirsManager ledgerDirsManager) {
super("BookieJournal-" + conf.getBookiePort());
this.ledgerDirsManager = ledgerDirsManager;
this.conf = conf;
this.journalDirectory = Bookie.getCurrentDirectory(conf.getJournalDir());
this.maxJournalSize = conf.getMaxJournalSize() * MB;
this.maxBackupJournals = conf.getMaxBackupJournals();
// read last log mark
lastLogMark.readLog();
LOG.debug("Last Log Mark : {}", lastLogMark);
}
LastLogMark getLastLogMark() {
return lastLogMark;
}
/**
* Records a LastLogMark in memory.
*
*
* The LastLogMark contains two parts: first one is txnLogId
* (file id of a journal) and the second one is txnLogPos (offset in
* a journal). The LastLogMark indicates that those entries before
* it have been persisted to both index and entry log files.
*
*
*
* This method is called before flushing entry log files and ledger cache.
*
*/
public void markLog() {
lastLogMark.markLog();
}
/**
* Persists the LastLogMark marked by #markLog() to disk.
*
*
* This action means entries added before LastLogMark whose entry data
* and index pages were already persisted to disk. It is the time to safely
* remove journal files created earlier than LastLogMark.txnLogId.
*
*
* If the bookie has crashed before persisting LastLogMark to disk,
* it still has journal files contains entries for which index pages may not
* have been persisted. Consequently, when the bookie restarts, it inspects
* journal files to restore those entries; data isn't lost.
*
*
* This method is called after flushing entry log files and ledger cache successfully, which is to ensure LastLogMark is pesisted.
*
* @see #markLog()
*/
public void rollLog() throws NoWritableLedgerDirException {
lastLogMark.rollLog();
}
/**
* Garbage collect older journals
*/
public void gcJournals() {
// list the journals that have been marked
List logs = listJournalIds(journalDirectory, new JournalRollingFilter());
// keep MAX_BACKUP_JOURNALS journal files before marked journal
if (logs.size() >= maxBackupJournals) {
int maxIdx = logs.size() - maxBackupJournals;
for (int i=0; i logs = listJournalIds(journalDirectory, new JournalIdFilter() {
@Override
public boolean accept(long journalId) {
if (journalId < markedLogId) {
return false;
}
return true;
}
});
// last log mark may be missed due to no sync up before
// validate filtered log ids only when we have markedLogId
if (markedLogId > 0) {
if (logs.size() == 0 || logs.get(0) != markedLogId) {
throw new IOException("Recovery log " + markedLogId + " is missing");
}
}
LOG.debug("Try to relay journal logs : {}", logs);
// TODO: When reading in the journal logs that need to be synced, we
// should use BufferedChannels instead to minimize the amount of
// system calls done.
for(Long id: logs) {
long logPosition = 0L;
if(id == markedLogId) {
logPosition = lastLogMark.getTxnLogPosition();
}
LOG.info("Replaying journal {} from position {}", id, logPosition);
scanJournal(id, logPosition, scanner);
}
}
/**
* record an add entry operation in journal
*/
public void logAddEntry(ByteBuffer entry, WriteCallback cb, Object ctx) {
long ledgerId = entry.getLong();
long entryId = entry.getLong();
entry.rewind();
queue.add(new QueueEntry(entry, ledgerId, entryId, cb, ctx));
}
/**
* Get the length of journal entries queue.
*
* @return length of journal entry queue.
*/
public int getJournalQueueLength() {
return queue.size();
}
/**
* A thread used for persisting journal entries to journal files.
*
*
* Besides persisting journal entries, it also takes responsibility of
* rolling journal files when a journal file reaches journal file size
* limitation.
*
*
* During journal rolling, it first closes the writing journal, generates
* new journal file using current timestamp, and continue persistence logic.
* Those journals will be garbage collected in SyncThread.
*
* @see Bookie#SyncThread
*/
@Override
public void run() {
LinkedList toFlush = new LinkedList();
ByteBuffer lenBuff = ByteBuffer.allocate(4);
JournalChannel logFile = null;
try {
List journalIds = listJournalIds(journalDirectory, null);
// Should not use MathUtils.now(), which use System.nanoTime() and
// could only be used to measure elapsed time.
// http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/System.html#nanoTime%28%29
long logId = journalIds.isEmpty() ? System.currentTimeMillis() : journalIds.get(journalIds.size() - 1);
BufferedChannel bc = null;
long lastFlushPosition = 0;
QueueEntry qe = null;
while (true) {
// new journal file to write
if (null == logFile) {
logId = logId + 1;
logFile = new JournalChannel(journalDirectory, logId);
bc = logFile.getBufferedChannel();
lastFlushPosition = 0;
}
if (qe == null) {
if (toFlush.isEmpty()) {
qe = queue.take();
} else {
qe = queue.poll();
if (qe == null || bc.position() > lastFlushPosition + 512*1024) {
//logFile.force(false);
bc.flush(true);
lastFlushPosition = bc.position();
lastLogMark.setLastLogMark(logId, lastFlushPosition);
for (QueueEntry e : toFlush) {
e.cb.writeComplete(BookieException.Code.OK,
e.ledgerId, e.entryId, null, e.ctx);
}
toFlush.clear();
// check whether journal file is over file limit
if (bc.position() > maxJournalSize) {
logFile.close();
logFile = null;
continue;
}
}
}
}
if (!running) {
LOG.info("Journal Manager is asked to shut down, quit.");
break;
}
if (qe == null) { // no more queue entry
continue;
}
lenBuff.clear();
lenBuff.putInt(qe.entry.remaining());
lenBuff.flip();
//
// we should be doing the following, but then we run out of
// direct byte buffers
// logFile.write(new ByteBuffer[] { lenBuff, qe.entry });
bc.write(lenBuff);
bc.write(qe.entry);
logFile.preAllocIfNeeded();
toFlush.add(qe);
qe = null;
}
logFile.close();
logFile = null;
} catch (IOException ioe) {
LOG.error("I/O exception in Journal thread!", ioe);
} catch (InterruptedException ie) {
LOG.warn("Journal exits when shutting down", ie);
} finally {
IOUtils.close(LOG, logFile);
}
}
/**
* Shuts down the journal.
*/
public synchronized void shutdown() {
try {
if (!running) {
return;
}
running = false;
this.interrupt();
this.join();
} catch (InterruptedException ie) {
LOG.warn("Interrupted during shutting down journal : ", ie);
}
}
private static int fullRead(JournalChannel fc, ByteBuffer bb) throws IOException {
int total = 0;
while(bb.remaining() > 0) {
int rc = fc.read(bb);
if (rc <= 0) {
return total;
}
total += rc;
}
return total;
}
}
JournalChannel.java 0000664 0000000 0000000 00000012544 12445073612 0034462 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /*
*
* 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.bookie;
import java.util.Arrays;
import java.io.Closeable;
import java.io.File;
import java.io.RandomAccessFile;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.ByteBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Simple wrapper around FileChannel to add versioning
* information to the file.
*/
class JournalChannel implements Closeable {
static Logger LOG = LoggerFactory.getLogger(JournalChannel.class);
final FileChannel fc;
final BufferedChannel bc;
final int formatVersion;
long nextPrealloc = 0;
final byte[] MAGIC_WORD = "BKLG".getBytes();
private final static int START_OF_FILE = -12345;
int HEADER_SIZE = 8; // 4byte magic word, 4 byte version
int MIN_COMPAT_JOURNAL_FORMAT_VERSION = 1;
int CURRENT_JOURNAL_FORMAT_VERSION = 4;
public final static long preAllocSize = 4*1024*1024;
public final static ByteBuffer zeros = ByteBuffer.allocate(512);
JournalChannel(File journalDirectory, long logId) throws IOException {
this(journalDirectory, logId, START_OF_FILE);
}
JournalChannel(File journalDirectory, long logId, long position) throws IOException {
File fn = new File(journalDirectory, Long.toHexString(logId) + ".txn");
LOG.info("Opening journal {}", fn);
if (!fn.exists()) { // new file, write version
if (!fn.createNewFile()) {
LOG.error("Journal file {}, that shouldn't exist, already exists. "
+ " is there another bookie process running?", fn);
throw new IOException("File " + fn
+ " suddenly appeared, is another bookie process running?");
}
fc = new RandomAccessFile(fn, "rw").getChannel();
formatVersion = CURRENT_JOURNAL_FORMAT_VERSION;
ByteBuffer bb = ByteBuffer.allocate(HEADER_SIZE);
bb.put(MAGIC_WORD);
bb.putInt(formatVersion);
bb.flip();
fc.write(bb);
fc.force(true);
bc = new BufferedChannel(fc, 65536);
nextPrealloc = preAllocSize;
fc.write(zeros, nextPrealloc);
} else { // open an existing file
fc = new RandomAccessFile(fn, "r").getChannel();
bc = null; // readonly
ByteBuffer bb = ByteBuffer.allocate(HEADER_SIZE);
int c = fc.read(bb);
bb.flip();
if (c == HEADER_SIZE) {
byte[] first4 = new byte[4];
bb.get(first4);
if (Arrays.equals(first4, MAGIC_WORD)) {
formatVersion = bb.getInt();
} else {
// pre magic word journal, reset to 0;
formatVersion = 1;
}
} else {
// no header, must be old version
formatVersion = 1;
}
if (formatVersion < MIN_COMPAT_JOURNAL_FORMAT_VERSION
|| formatVersion > CURRENT_JOURNAL_FORMAT_VERSION) {
String err = String.format("Invalid journal version, unable to read."
+ " Expected between (%d) and (%d), got (%d)",
MIN_COMPAT_JOURNAL_FORMAT_VERSION, CURRENT_JOURNAL_FORMAT_VERSION,
formatVersion);
LOG.error(err);
throw new IOException(err);
}
try {
if (position == START_OF_FILE) {
if (formatVersion >= 2) {
fc.position(HEADER_SIZE);
} else {
fc.position(0);
}
} else {
fc.position(position);
}
} catch (IOException e) {
LOG.error("Bookie journal file can seek to position :", e);
}
}
}
int getFormatVersion() {
return formatVersion;
}
BufferedChannel getBufferedChannel() throws IOException {
if (bc == null) {
throw new IOException("Read only journal channel");
}
return bc;
}
void preAllocIfNeeded() throws IOException {
if (bc.position() > nextPrealloc) {
nextPrealloc = ((fc.size() + HEADER_SIZE) / preAllocSize + 1) * preAllocSize;
zeros.clear();
fc.write(zeros, nextPrealloc);
}
}
int read(ByteBuffer dst)
throws IOException {
return fc.read(dst);
}
public void close() throws IOException {
fc.close();
}
}
LedgerCache.java 0000664 0000000 0000000 00000003445 12445073612 0033705 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /*
*
* 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.bookie;
import java.io.Closeable;
import java.io.IOException;
/**
* This class maps a ledger entry number into a location (entrylogid, offset) in
* an entry log file. It does user level caching to more efficiently manage disk
* head scheduling.
*/
interface LedgerCache extends Closeable {
boolean setFenced(long ledgerId) throws IOException;
boolean isFenced(long ledgerId) throws IOException;
void setMasterKey(long ledgerId, byte[] masterKey) throws IOException;
byte[] readMasterKey(long ledgerId) throws IOException, BookieException;
boolean ledgerExists(long ledgerId) throws IOException;
void putEntryOffset(long ledger, long entry, long offset) throws IOException;
long getEntryOffset(long ledger, long entry) throws IOException;
void flushLedger(boolean doAll) throws IOException;
long getLastEntry(long ledgerId) throws IOException;
void deleteLedger(long ledgerId) throws IOException;
LedgerCacheBean getJMXBean();
}
LedgerCacheBean.java 0000664 0000000 0000000 00000001747 12445073612 0034476 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /**
* 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.bookie;
import org.apache.bookkeeper.jmx.BKMBeanInfo;
/**
* Ledger Cache Bean
*/
public interface LedgerCacheBean extends LedgerCacheMXBean, BKMBeanInfo {
}
LedgerCacheImpl.java 0000664 0000000 0000000 00000101753 12445073612 0034530 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /*
*
* 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.bookie;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicBoolean;
import com.google.common.annotations.VisibleForTesting;
import org.apache.bookkeeper.util.SnapshotMap;
import org.apache.bookkeeper.bookie.LedgerDirsManager.LedgerDirsListener;
import org.apache.bookkeeper.bookie.LedgerDirsManager.NoWritableLedgerDirException;
import org.apache.bookkeeper.conf.ServerConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implementation of LedgerCache interface.
* This class serves two purposes.
*/
public class LedgerCacheImpl implements LedgerCache {
private final static Logger LOG = LoggerFactory.getLogger(LedgerCacheImpl.class);
private static final String IDX = ".idx";
static final String RLOC = ".rloc";
private LedgerDirsManager ledgerDirsManager;
final private AtomicBoolean shouldRelocateIndexFile = new AtomicBoolean(false);
public LedgerCacheImpl(ServerConfiguration conf, SnapshotMap activeLedgers,
LedgerDirsManager ledgerDirsManager)
throws IOException {
this.ledgerDirsManager = ledgerDirsManager;
this.openFileLimit = conf.getOpenFileLimit();
this.pageSize = conf.getPageSize();
this.entriesPerPage = pageSize / 8;
if (conf.getPageLimit() <= 0) {
// allocate half of the memory to the page cache
this.pageLimit = (int)((Runtime.getRuntime().maxMemory() / 3) / this.pageSize);
} else {
this.pageLimit = conf.getPageLimit();
}
LOG.info("maxMemory = " + Runtime.getRuntime().maxMemory());
LOG.info("openFileLimit is " + openFileLimit + ", pageSize is " + pageSize + ", pageLimit is " + pageLimit);
this.activeLedgers = activeLedgers;
// Retrieve all of the active ledgers.
getActiveLedgers();
ledgerDirsManager.addLedgerDirsListener(getLedgerDirsListener());
}
/**
* the list of potentially clean ledgers
*/
LinkedList cleanLedgers = new LinkedList();
/**
* the list of potentially dirty ledgers
*/
LinkedList dirtyLedgers = new LinkedList();
HashMap fileInfoCache = new HashMap();
LinkedList openLedgers = new LinkedList();
// Manage all active ledgers in LedgerManager
// so LedgerManager has knowledge to garbage collect inactive/deleted ledgers
final SnapshotMap activeLedgers;
final int openFileLimit;
final int pageSize;
final int pageLimit;
final int entriesPerPage;
/**
* @return page size used in ledger cache
*/
public int getPageSize() {
return pageSize;
}
/**
* @return entries per page used in ledger cache
*/
public int getEntriesPerPage() {
return entriesPerPage;
}
/**
* @return page limitation in ledger cache
*/
public int getPageLimit() {
return pageLimit;
}
// The number of pages that have actually been used
private int pageCount = 0;
HashMap> pages = new HashMap>();
/**
* @return number of page used in ledger cache
*/
public int getNumUsedPages() {
return pageCount;
}
private void putIntoTable(HashMap> table, LedgerEntryPage lep) {
HashMap map = table.get(lep.getLedger());
if (map == null) {
map = new HashMap();
table.put(lep.getLedger(), map);
}
map.put(lep.getFirstEntry(), lep);
}
private static LedgerEntryPage getFromTable(HashMap> table,
Long ledger, Long firstEntry) {
HashMap map = table.get(ledger);
if (map != null) {
return map.get(firstEntry);
}
return null;
}
synchronized protected LedgerEntryPage getLedgerEntryPage(Long ledger, Long firstEntry, boolean onlyDirty) {
LedgerEntryPage lep = getFromTable(pages, ledger, firstEntry);
if (lep == null) {
return null;
}
lep.usePage();
if (onlyDirty && lep.isClean()) {
return null;
} else {
return lep;
}
}
/**
* Grab ledger entry page whose first entry is pageEntry
.
*
* If the page doesn't existed before, we allocate a memory page.
* Otherwise, we grab a clean page and read it from disk.
*
* @param ledger
* Ledger Id
* @param pageEntry
* Start entry of this entry page.
*/
private LedgerEntryPage grabLedgerEntryPage(long ledger, long pageEntry) throws IOException {
LedgerEntryPage lep = grabCleanPage(ledger, pageEntry);
try {
// should update page before we put it into table
// otherwise we would put an empty page in it
updatePage(lep);
synchronized(this) {
putIntoTable(pages, lep);
}
} catch (IOException ie) {
// if we grab a clean page, but failed to update the page
// we are exhausting the count of ledger entry pages.
// since this page will be never used, so we need to decrement
// page count of ledger cache.
lep.releasePage();
synchronized (this) {
--pageCount;
}
throw ie;
}
return lep;
}
@Override
public void putEntryOffset(long ledger, long entry, long offset) throws IOException {
int offsetInPage = (int) (entry % entriesPerPage);
// find the id of the first entry of the page that has the entry
// we are looking for
long pageEntry = entry-offsetInPage;
LedgerEntryPage lep = getLedgerEntryPage(ledger, pageEntry, false);
if (lep == null) {
lep = grabLedgerEntryPage(ledger, pageEntry);
}
if (lep != null) {
lep.setOffset(offset, offsetInPage*8);
lep.releasePage();
return;
}
}
@Override
public long getEntryOffset(long ledger, long entry) throws IOException {
int offsetInPage = (int) (entry%entriesPerPage);
// find the id of the first entry of the page that has the entry
// we are looking for
long pageEntry = entry-offsetInPage;
LedgerEntryPage lep = getLedgerEntryPage(ledger, pageEntry, false);
try {
if (lep == null) {
lep = grabLedgerEntryPage(ledger, pageEntry);
}
return lep.getOffset(offsetInPage*8);
} finally {
if (lep != null) {
lep.releasePage();
}
}
}
@VisibleForTesting
public static final String getLedgerName(long ledgerId) {
int parent = (int) (ledgerId & 0xff);
int grandParent = (int) ((ledgerId & 0xff00) >> 8);
StringBuilder sb = new StringBuilder();
sb.append(Integer.toHexString(grandParent));
sb.append('/');
sb.append(Integer.toHexString(parent));
sb.append('/');
sb.append(Long.toHexString(ledgerId));
sb.append(IDX);
return sb.toString();
}
FileInfo getFileInfo(Long ledger, byte masterKey[]) throws IOException {
synchronized(fileInfoCache) {
FileInfo fi = fileInfoCache.get(ledger);
if (fi == null) {
File lf = findIndexFile(ledger);
if (lf == null) {
if (masterKey == null) {
throw new Bookie.NoLedgerException(ledger);
}
lf = getNewLedgerIndexFile(ledger, null);
// A new ledger index file has been created for this Bookie.
// Add this new ledger to the set of active ledgers.
LOG.debug("New ledger index file created for ledgerId: {}", ledger);
activeLedgers.put(ledger, true);
}
evictFileInfoIfNecessary();
fi = new FileInfo(lf, masterKey);
fileInfoCache.put(ledger, fi);
openLedgers.add(ledger);
}
if (fi != null) {
fi.use();
}
return fi;
}
}
/**
* Get a new index file for ledger excluding directory excludedDir
.
*
* @param ledger
* Ledger id.
* @param excludedDir
* The ledger directory to exclude.
* @return new index file object.
* @throws NoWritableLedgerDirException if there is no writable dir available.
*/
private File getNewLedgerIndexFile(Long ledger, File excludedDir)
throws NoWritableLedgerDirException {
File dir = ledgerDirsManager.pickRandomWritableDir(excludedDir);
String ledgerName = getLedgerName(ledger);
return new File(dir, ledgerName);
}
private void updatePage(LedgerEntryPage lep) throws IOException {
if (!lep.isClean()) {
throw new IOException("Trying to update a dirty page");
}
FileInfo fi = null;
try {
fi = getFileInfo(lep.getLedger(), null);
long pos = lep.getFirstEntry()*8;
if (pos >= fi.size()) {
lep.zeroPage();
} else {
lep.readPage(fi);
}
} finally {
if (fi != null) {
fi.release();
}
}
}
private LedgerDirsListener getLedgerDirsListener() {
return new LedgerDirsListener() {
@Override
public void diskFull(File disk) {
// If the current entry log disk is full, then create new entry
// log.
shouldRelocateIndexFile.set(true);
}
@Override
public void diskFailed(File disk) {
// Nothing to handle here. Will be handled in Bookie
}
@Override
public void allDisksFull() {
// Nothing to handle here. Will be handled in Bookie
}
@Override
public void fatalError() {
// Nothing to handle here. Will be handled in Bookie
}
};
}
@Override
public void flushLedger(boolean doAll) throws IOException {
synchronized(dirtyLedgers) {
if (dirtyLedgers.isEmpty()) {
synchronized(this) {
for(Long l: pages.keySet()) {
if (LOG.isTraceEnabled()) {
LOG.trace("Adding {} to dirty pages", Long.toHexString(l));
}
dirtyLedgers.add(l);
}
}
}
if (dirtyLedgers.isEmpty()) {
return;
}
if (shouldRelocateIndexFile.get()) {
// if some new dir detected as full, then move all corresponding
// open index files to new location
for (Long l : dirtyLedgers) {
FileInfo fi = null;
try {
fi = getFileInfo(l, null);
File currentDir = getLedgerDirForLedger(fi);
if (ledgerDirsManager.isDirFull(currentDir)) {
moveLedgerIndexFile(l, fi);
}
} finally {
if (null != fi) {
fi.release();
}
}
}
shouldRelocateIndexFile.set(false);
}
while(!dirtyLedgers.isEmpty()) {
Long l = dirtyLedgers.removeFirst();
flushLedger(l);
if (!doAll) {
break;
}
// Yield. if we are doing all the ledgers we don't want to block other flushes that
// need to happen
try {
dirtyLedgers.wait(1);
} catch (InterruptedException e) {
// just pass it on
Thread.currentThread().interrupt();
}
}
}
}
/**
* Get the ledger directory that the ledger index belongs to.
*
* @param fi File info of a ledger
* @return ledger directory that the ledger belongs to.
*/
private File getLedgerDirForLedger(FileInfo fi) {
return fi.getLf().getParentFile().getParentFile().getParentFile();
}
private void moveLedgerIndexFile(Long l, FileInfo fi) throws NoWritableLedgerDirException, IOException {
File newLedgerIndexFile = getNewLedgerIndexFile(l, getLedgerDirForLedger(fi));
fi.moveToNewLocation(newLedgerIndexFile, fi.getSizeSinceLastwrite());
}
/**
* Flush a specified ledger
*
* @param l
* Ledger Id
* @throws IOException
*/
private void flushLedger(long l) throws IOException {
FileInfo fi = null;
try {
fi = getFileInfo(l, null);
flushLedger(l, fi);
} catch (Bookie.NoLedgerException nle) {
// ledger has been deleted
} finally {
if (null != fi) {
fi.release();
}
}
}
private void flushLedger(long l, FileInfo fi) throws IOException {
LinkedList firstEntryList;
synchronized(this) {
HashMap pageMap = pages.get(l);
if (pageMap == null || pageMap.isEmpty()) {
fi.flushHeader();
return;
}
firstEntryList = new LinkedList();
for(Map.Entry entry: pageMap.entrySet()) {
LedgerEntryPage lep = entry.getValue();
if (lep.isClean()) {
LOG.trace("Page is clean {}", lep);
continue;
}
firstEntryList.add(lep.getFirstEntry());
}
}
if (firstEntryList.size() == 0) {
LOG.debug("Nothing to flush for ledger {}.", l);
// nothing to do
return;
}
// Now flush all the pages of a ledger
List entries = new ArrayList(firstEntryList.size());
try {
for(Long firstEntry: firstEntryList) {
LedgerEntryPage lep = getLedgerEntryPage(l, firstEntry, true);
if (lep != null) {
entries.add(lep);
}
}
Collections.sort(entries, new Comparator() {
@Override
public int compare(LedgerEntryPage o1, LedgerEntryPage o2) {
return (int)(o1.getFirstEntry()-o2.getFirstEntry());
}
});
ArrayList versions = new ArrayList(entries.size());
// flush the header if necessary
fi.flushHeader();
int start = 0;
long lastOffset = -1;
for(int i = 0; i < entries.size(); i++) {
versions.add(i, entries.get(i).getVersion());
if (lastOffset != -1 && (entries.get(i).getFirstEntry() - lastOffset) != entriesPerPage) {
// send up a sequential list
int count = i - start;
if (count == 0) {
LOG.warn("Count cannot possibly be zero!");
}
writeBuffers(l, entries, fi, start, count);
start = i;
}
lastOffset = entries.get(i).getFirstEntry();
}
if (entries.size()-start == 0 && entries.size() != 0) {
LOG.warn("Nothing to write, but there were entries!");
}
writeBuffers(l, entries, fi, start, entries.size()-start);
synchronized(this) {
for(int i = 0; i < entries.size(); i++) {
LedgerEntryPage lep = entries.get(i);
lep.setClean(versions.get(i));
}
}
} finally {
for(LedgerEntryPage lep: entries) {
lep.releasePage();
}
}
}
private void writeBuffers(Long ledger,
List entries, FileInfo fi,
int start, int count) throws IOException {
if (LOG.isTraceEnabled()) {
LOG.trace("Writing {} buffers of {}", count, Long.toHexString(ledger));
}
if (count == 0) {
return;
}
ByteBuffer buffs[] = new ByteBuffer[count];
for(int j = 0; j < count; j++) {
buffs[j] = entries.get(start+j).getPageToWrite();
if (entries.get(start+j).getLedger() != ledger) {
throw new IOException("Writing to " + ledger + " but page belongs to "
+ entries.get(start+j).getLedger());
}
}
long totalWritten = 0;
while(buffs[buffs.length-1].remaining() > 0) {
long rc = fi.write(buffs, entries.get(start+0).getFirstEntry()*8);
if (rc <= 0) {
throw new IOException("Short write to ledger " + ledger + " rc = " + rc);
}
totalWritten += rc;
}
if (totalWritten != (long)count * (long)pageSize) {
throw new IOException("Short write to ledger " + ledger + " wrote " + totalWritten
+ " expected " + count * pageSize);
}
}
private LedgerEntryPage grabCleanPage(long ledger, long entry) throws IOException {
if (entry % entriesPerPage != 0) {
throw new IllegalArgumentException(entry + " is not a multiple of " + entriesPerPage);
}
outerLoop:
while(true) {
synchronized(this) {
if (pageCount < pageLimit) {
// let's see if we can allocate something
LedgerEntryPage lep = new LedgerEntryPage(pageSize, entriesPerPage);
lep.setLedger(ledger);
lep.setFirstEntry(entry);
// note, this will not block since it is a new page
lep.usePage();
pageCount++;
return lep;
}
}
synchronized(cleanLedgers) {
if (cleanLedgers.isEmpty()) {
flushLedger(false);
synchronized(this) {
for(Long l: pages.keySet()) {
cleanLedgers.add(l);
}
}
}
synchronized(this) {
// if ledgers deleted between checking pageCount and putting
// ledgers into cleanLedgers list, the cleanLedgers list would be empty.
// so give it a chance to go back to check pageCount again because
// deleteLedger would decrement pageCount to return the number of pages
// occupied by deleted ledgers.
if (cleanLedgers.isEmpty()) {
continue outerLoop;
}
Long cleanLedger = cleanLedgers.getFirst();
Map map = pages.get(cleanLedger);
while (map == null || map.isEmpty()) {
cleanLedgers.removeFirst();
if (cleanLedgers.isEmpty()) {
continue outerLoop;
}
cleanLedger = cleanLedgers.getFirst();
map = pages.get(cleanLedger);
}
Iterator> it = map.entrySet().iterator();
LedgerEntryPage lep = it.next().getValue();
while((lep.inUse() || !lep.isClean())) {
if (!it.hasNext()) {
// no clean page found in this ledger
cleanLedgers.removeFirst();
continue outerLoop;
}
lep = it.next().getValue();
}
it.remove();
if (map.isEmpty()) {
pages.remove(lep.getLedger());
}
lep.usePage();
lep.zeroPage();
lep.setLedger(ledger);
lep.setFirstEntry(entry);
return lep;
}
}
}
}
@Override
public long getLastEntry(long ledgerId) throws IOException {
long lastEntry = 0;
// Find the last entry in the cache
synchronized(this) {
Map map = pages.get(ledgerId);
if (map != null) {
for(LedgerEntryPage lep: map.values()) {
if (lep.getFirstEntry() + entriesPerPage < lastEntry) {
continue;
}
lep.usePage();
long highest = lep.getLastEntry();
if (highest > lastEntry) {
lastEntry = highest;
}
lep.releasePage();
}
}
}
FileInfo fi = null;
try {
fi = getFileInfo(ledgerId, null);
long size = fi.size();
// make sure the file size is aligned with index entry size
// otherwise we may read incorret data
if (0 != size % 8) {
LOG.warn("Index file of ledger {} is not aligned with index entry size.", ledgerId);
size = size - size % 8;
}
// we may not have the last entry in the cache
if (size > lastEntry*8) {
ByteBuffer bb = ByteBuffer.allocate(getPageSize());
long position = size - getPageSize();
if (position < 0) {
position = 0;
}
fi.read(bb, position);
bb.flip();
long startingEntryId = position/8;
for(int i = getEntriesPerPage()-1; i >= 0; i--) {
if (bb.getLong(i*8) != 0) {
if (lastEntry < startingEntryId+i) {
lastEntry = startingEntryId+i;
}
break;
}
}
}
} finally {
if (fi != null) {
fi.release();
}
}
return lastEntry;
}
/**
* This method will look within the ledger directories for the ledger index
* files. That will comprise the set of active ledgers this particular
* BookieServer knows about that have not yet been deleted by the BookKeeper
* Client. This is called only once during initialization.
*/
private void getActiveLedgers() throws IOException {
// Ledger index files are stored in a file hierarchy with a parent and
// grandParent directory. We'll have to go two levels deep into these
// directories to find the index files.
for (File ledgerDirectory : ledgerDirsManager.getAllLedgerDirs()) {
for (File grandParent : ledgerDirectory.listFiles()) {
if (grandParent.isDirectory()) {
for (File parent : grandParent.listFiles()) {
if (parent.isDirectory()) {
for (File index : parent.listFiles()) {
if (!index.isFile()
|| (!index.getName().endsWith(IDX) && !index.getName().endsWith(RLOC))) {
continue;
}
// We've found a ledger index file. The file
// name is the HexString representation of the
// ledgerId.
String ledgerIdInHex = index.getName().replace(RLOC, "").replace(IDX, "");
if (index.getName().endsWith(RLOC)) {
if (findIndexFile(Long.parseLong(ledgerIdInHex)) != null) {
if (!index.delete()) {
LOG.warn("Deleting the rloc file " + index + " failed");
}
continue;
} else {
File dest = new File(index.getParentFile(), ledgerIdInHex + IDX);
if (!index.renameTo(dest)) {
throw new IOException("Renaming rloc file " + index
+ " to index file has failed");
}
}
}
activeLedgers.put(Long.parseLong(ledgerIdInHex, 16), true);
}
}
}
}
}
}
}
/**
* This method is called whenever a ledger is deleted by the BookKeeper Client
* and we want to remove all relevant data for it stored in the LedgerCache.
*/
@Override
public void deleteLedger(long ledgerId) throws IOException {
LOG.debug("Deleting ledgerId: {}", ledgerId);
// remove pages first to avoid page flushed when deleting file info
synchronized(this) {
Map lpages = pages.remove(ledgerId);
if (null != lpages) {
pageCount -= lpages.size();
if (pageCount < 0) {
LOG.error("Page count of ledger cache has been decremented to be less than zero.");
}
}
}
// Delete the ledger's index file and close the FileInfo
FileInfo fi = null;
try {
fi = getFileInfo(ledgerId, null);
fi.close(false);
fi.delete();
} finally {
// should release use count
// otherwise the file channel would not be closed.
if (null != fi) {
fi.release();
}
}
// Remove it from the active ledger manager
activeLedgers.remove(ledgerId);
// Now remove it from all the other lists and maps.
// These data structures need to be synchronized first before removing entries.
synchronized(fileInfoCache) {
fileInfoCache.remove(ledgerId);
}
synchronized(cleanLedgers) {
cleanLedgers.remove(ledgerId);
}
synchronized(dirtyLedgers) {
dirtyLedgers.remove(ledgerId);
}
synchronized(openLedgers) {
openLedgers.remove(ledgerId);
}
}
private File findIndexFile(long ledgerId) throws IOException {
String ledgerName = getLedgerName(ledgerId);
for (File d : ledgerDirsManager.getAllLedgerDirs()) {
File lf = new File(d, ledgerName);
if (lf.exists()) {
return lf;
}
}
return null;
}
@Override
public byte[] readMasterKey(long ledgerId) throws IOException, BookieException {
synchronized(fileInfoCache) {
FileInfo fi = fileInfoCache.get(ledgerId);
if (fi == null) {
File lf = findIndexFile(ledgerId);
if (lf == null) {
throw new Bookie.NoLedgerException(ledgerId);
}
evictFileInfoIfNecessary();
fi = new FileInfo(lf, null);
byte[] key = fi.getMasterKey();
fileInfoCache.put(ledgerId, fi);
openLedgers.add(ledgerId);
return key;
}
return fi.getMasterKey();
}
}
// evict file info if necessary
private void evictFileInfoIfNecessary() throws IOException {
synchronized (fileInfoCache) {
if (openLedgers.size() > openFileLimit) {
long ledgerToRemove = openLedgers.removeFirst();
// TODO Add a statistic here, we don't care really which
// ledger is evicted, but the rate at which they get evicted
LOG.debug("Ledger {} is evicted from file info cache.",
ledgerToRemove);
FileInfo fi = fileInfoCache.remove(ledgerToRemove);
if (fi != null) {
fi.close(true);
}
}
}
}
@Override
public boolean setFenced(long ledgerId) throws IOException {
FileInfo fi = null;
try {
fi = getFileInfo(ledgerId, null);
if (null != fi) {
return fi.setFenced();
}
return false;
} finally {
if (null != fi) {
fi.release();
}
}
}
@Override
public boolean isFenced(long ledgerId) throws IOException {
FileInfo fi = null;
try {
fi = getFileInfo(ledgerId, null);
if (null != fi) {
return fi.isFenced();
}
return false;
} finally {
if (null != fi) {
fi.release();
}
}
}
@Override
public void setMasterKey(long ledgerId, byte[] masterKey) throws IOException {
FileInfo fi = null;
try {
fi = getFileInfo(ledgerId, masterKey);
} finally {
if (null != fi) {
fi.release();
}
}
}
@Override
public boolean ledgerExists(long ledgerId) throws IOException {
synchronized(fileInfoCache) {
FileInfo fi = fileInfoCache.get(ledgerId);
if (fi == null) {
File lf = findIndexFile(ledgerId);
if (lf == null) {
return false;
}
}
}
return true;
}
@Override
public LedgerCacheBean getJMXBean() {
return new LedgerCacheBean() {
@Override
public String getName() {
return "LedgerCache";
}
@Override
public boolean isHidden() {
return false;
}
@Override
public int getPageCount() {
return LedgerCacheImpl.this.getNumUsedPages();
}
@Override
public int getPageSize() {
return LedgerCacheImpl.this.getPageSize();
}
@Override
public int getOpenFileLimit() {
return openFileLimit;
}
@Override
public int getPageLimit() {
return LedgerCacheImpl.this.getPageLimit();
}
@Override
public int getNumCleanLedgers() {
return cleanLedgers.size();
}
@Override
public int getNumDirtyLedgers() {
return dirtyLedgers.size();
}
@Override
public int getNumOpenLedgers() {
return openLedgers.size();
}
};
}
@Override
public void close() throws IOException {
synchronized (fileInfoCache) {
for (Entry fileInfo : fileInfoCache.entrySet()) {
FileInfo value = fileInfo.getValue();
if (value != null) {
value.close(true);
}
}
fileInfoCache.clear();
}
}
}
LedgerCacheMXBean.java 0000664 0000000 0000000 00000003001 12445073612 0034724 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /**
* 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.bookie;
/**
* Ledger Cache MBean
*/
public interface LedgerCacheMXBean {
/**
* @return number of page used in cache
*/
public int getPageCount();
/**
* @return page size
*/
public int getPageSize();
/**
* @return the limit of open files
*/
public int getOpenFileLimit();
/**
* @return the limit number of pages
*/
public int getPageLimit();
/**
* @return number of clean ledgers
*/
public int getNumCleanLedgers();
/**
* @return number of dirty ledgers
*/
public int getNumDirtyLedgers();
/**
* @return number of open ledgers
*/
public int getNumOpenLedgers();
}
LedgerDescriptor.java 0000664 0000000 0000000 00000004406 12445073612 0035016 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /*
*
* 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.bookie;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implements a ledger inside a bookie. In particular, it implements operations
* to write entries to a ledger and read entries from a ledger.
*/
public abstract class LedgerDescriptor {
static LedgerDescriptor create(byte[] masterKey,
long ledgerId,
LedgerStorage ledgerStorage) throws IOException {
LedgerDescriptor ledger = new LedgerDescriptorImpl(masterKey, ledgerId, ledgerStorage);
ledgerStorage.setMasterKey(ledgerId, masterKey);
return ledger;
}
static LedgerDescriptor createReadOnly(long ledgerId,
LedgerStorage ledgerStorage)
throws IOException, Bookie.NoLedgerException {
if (!ledgerStorage.ledgerExists(ledgerId)) {
throw new Bookie.NoLedgerException(ledgerId);
}
return new LedgerDescriptorReadOnlyImpl(ledgerId, ledgerStorage);
}
abstract void checkAccess(byte masterKey[]) throws BookieException, IOException;
abstract long getLedgerId();
abstract boolean setFenced() throws IOException;
abstract boolean isFenced() throws IOException;
abstract long addEntry(ByteBuffer entry) throws IOException;
abstract ByteBuffer readEntry(long entryId) throws IOException;
}
LedgerDescriptorImpl.java 0000664 0000000 0000000 00000005123 12445073612 0035635 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /*
*
* 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.bookie;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implements a ledger inside a bookie. In particular, it implements operations
* to write entries to a ledger and read entries from a ledger.
*
*/
public class LedgerDescriptorImpl extends LedgerDescriptor {
final static Logger LOG = LoggerFactory.getLogger(LedgerDescriptor.class);
final LedgerStorage ledgerStorage;
private long ledgerId;
final byte[] masterKey;
LedgerDescriptorImpl(byte[] masterKey, long ledgerId, LedgerStorage ledgerStorage) {
this.masterKey = masterKey;
this.ledgerId = ledgerId;
this.ledgerStorage = ledgerStorage;
}
@Override
void checkAccess(byte masterKey[]) throws BookieException, IOException {
if (!Arrays.equals(this.masterKey, masterKey)) {
throw BookieException.create(BookieException.Code.UnauthorizedAccessException);
}
}
@Override
public long getLedgerId() {
return ledgerId;
}
@Override
boolean setFenced() throws IOException {
return ledgerStorage.setFenced(ledgerId);
}
@Override
boolean isFenced() throws IOException {
return ledgerStorage.isFenced(ledgerId);
}
@Override
long addEntry(ByteBuffer entry) throws IOException {
long ledgerId = entry.getLong();
if (ledgerId != this.ledgerId) {
throw new IOException("Entry for ledger " + ledgerId + " was sent to " + this.ledgerId);
}
entry.rewind();
return ledgerStorage.addEntry(entry);
}
@Override
ByteBuffer readEntry(long entryId) throws IOException {
return ledgerStorage.getEntry(ledgerId, entryId);
}
}
LedgerDescriptorReadOnlyImpl.java 0000664 0000000 0000000 00000003367 12445073612 0037303 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /*
*
* 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.bookie;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* Implements a ledger inside a bookie. In particular, it implements operations
* to write entries to a ledger and read entries from a ledger.
*/
public class LedgerDescriptorReadOnlyImpl extends LedgerDescriptorImpl {
LedgerDescriptorReadOnlyImpl(long ledgerId, LedgerStorage storage) {
super(null, ledgerId, storage);
}
@Override
boolean setFenced() throws IOException {
assert false;
throw new IOException("Invalid action on read only descriptor");
}
@Override
long addEntry(ByteBuffer entry) throws IOException {
assert false;
throw new IOException("Invalid action on read only descriptor");
}
@Override
void checkAccess(byte masterKey[]) throws BookieException, IOException {
assert false;
throw new IOException("Invalid action on read only descriptor");
}
}
LedgerDirsManager.java 0000664 0000000 0000000 00000022060 12445073612 0035070 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /**
*
* 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.bookie;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import com.google.common.annotations.VisibleForTesting;
import org.apache.bookkeeper.conf.ServerConfiguration;
import org.apache.bookkeeper.util.DiskChecker;
import org.apache.bookkeeper.util.DiskChecker.DiskErrorException;
import org.apache.bookkeeper.util.DiskChecker.DiskOutOfSpaceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class manages ledger directories used by the bookie.
*/
public class LedgerDirsManager {
private static Logger LOG = LoggerFactory
.getLogger(LedgerDirsManager.class);
private volatile List filledDirs;
private final List ledgerDirectories;
private volatile List writableLedgerDirectories;
private DiskChecker diskChecker;
private List listeners;
private LedgerDirsMonitor monitor;
private final Random rand = new Random();
public LedgerDirsManager(ServerConfiguration conf) {
this.ledgerDirectories = Arrays.asList(Bookie
.getCurrentDirectories(conf.getLedgerDirs()));
this.writableLedgerDirectories = new ArrayList(ledgerDirectories);
this.filledDirs = new ArrayList();
listeners = new ArrayList();
diskChecker = new DiskChecker(conf.getDiskUsageThreshold());
monitor = new LedgerDirsMonitor(conf.getDiskCheckInterval());
}
/**
* Get all ledger dirs configured
*/
public List getAllLedgerDirs() {
return ledgerDirectories;
}
/**
* Get only writable ledger dirs.
*/
public List getWritableLedgerDirs()
throws NoWritableLedgerDirException {
if (writableLedgerDirectories.isEmpty()) {
String errMsg = "All ledger directories are non writable";
NoWritableLedgerDirException e = new NoWritableLedgerDirException(
errMsg);
LOG.error(errMsg, e);
throw e;
}
return writableLedgerDirectories;
}
/**
* Get dirs, which are full more than threshold
*/
public boolean isDirFull(File dir) {
return filledDirs.contains(dir);
}
/**
* Add the dir to filled dirs list
*/
@VisibleForTesting
public void addToFilledDirs(File dir) {
if (!filledDirs.contains(dir)) {
LOG.warn(dir + " is out of space."
+ " Adding it to filled dirs list");
// Update filled dirs list
List updatedFilledDirs = new ArrayList(filledDirs);
updatedFilledDirs.add(dir);
filledDirs = updatedFilledDirs;
// Update the writable ledgers list
List newDirs = new ArrayList(writableLedgerDirectories);
newDirs.removeAll(filledDirs);
writableLedgerDirectories = newDirs;
// Notify listeners about disk full
for (LedgerDirsListener listener : listeners) {
listener.diskFull(dir);
}
}
}
/**
* Returns one of the ledger dir from writable dirs list randomly.
*/
File pickRandomWritableDir() throws NoWritableLedgerDirException {
return pickRandomWritableDir(null);
}
/**
* Pick up a writable dir from available dirs list randomly. The excludedDir
* will not be pickedup.
*
* @param excludedDir
* The directory to exclude during pickup.
* @throws NoWritableLedgerDirException if there is no writable dir available.
*/
File pickRandomWritableDir(File excludedDir) throws NoWritableLedgerDirException {
List writableDirs = getWritableLedgerDirs();
final int start = rand.nextInt(writableDirs.size());
int idx = start;
File candidate = writableDirs.get(idx);
while (null != excludedDir && excludedDir.equals(candidate)) {
idx = (idx + 1) % writableDirs.size();
if (idx == start) {
// after searching all available dirs,
// no writable dir is found
throw new NoWritableLedgerDirException("No writable directories found from "
+ " available writable dirs (" + writableDirs + ") : exclude dir "
+ excludedDir);
}
candidate = writableDirs.get(idx);
}
return candidate;
}
public void addLedgerDirsListener(LedgerDirsListener listener) {
if (listener != null) {
listeners.add(listener);
}
}
// start the daemon for disk monitoring
public void start() {
monitor.setDaemon(true);
monitor.start();
}
// shutdown disk monitoring daemon
public void shutdown() {
monitor.interrupt();
try {
monitor.join();
} catch (InterruptedException e) {
// Ignore
}
}
/**
* Thread to monitor the disk space periodically.
*/
private class LedgerDirsMonitor extends Thread {
private final int interval;
public LedgerDirsMonitor(int interval) {
super("LedgerDirsMonitorThread");
this.interval = interval;
}
@Override
public void run() {
try {
while (true) {
List writableDirs;
try {
writableDirs = getWritableLedgerDirs();
} catch (NoWritableLedgerDirException e) {
for (LedgerDirsListener listener : listeners) {
listener.allDisksFull();
}
break;
}
// Check all writable dirs disk space usage.
for (File dir : writableDirs) {
try {
diskChecker.checkDir(dir);
} catch (DiskErrorException e) {
// Notify disk failure to all listeners
for (LedgerDirsListener listener : listeners) {
listener.diskFailed(dir);
}
} catch (DiskOutOfSpaceException e) {
// Notify disk full to all listeners
addToFilledDirs(dir);
}
}
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
LOG.info("LedgerDirsMonitor thread is interrupted");
break;
}
}
} catch (Exception e) {
LOG.error("Error Occured while checking disks", e);
// Notify disk failure to all listeners
for (LedgerDirsListener listener : listeners) {
listener.fatalError();
}
}
LOG.info("LedgerDirsMonitorThread exited!");
}
}
/**
* Indicates All configured ledger directories are full.
*/
public static class NoWritableLedgerDirException extends IOException {
private static final long serialVersionUID = -8696901285061448421L;
public NoWritableLedgerDirException(String errMsg) {
super(errMsg);
}
}
/**
* Listener for the disk check events will be notified from the
* {@link LedgerDirsManager} whenever disk full/failure detected.
*/
public static interface LedgerDirsListener {
/**
* This will be notified on disk failure/disk error
*
* @param disk
* Failed disk
*/
void diskFailed(File disk);
/**
* This will be notified on disk detected as full
*
* @param disk
* Filled disk
*/
void diskFull(File disk);
/**
* This will be notified whenever all disks are detected as full.
*/
void allDisksFull();
/**
* This will notify the fatal errors.
*/
void fatalError();
}
}
LedgerEntryPage.java 0000664 0000000 0000000 00000011507 12445073612 0034576 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /*
*
* 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.bookie;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.apache.bookkeeper.proto.BookieProtocol;
/**
* This is a page in the LedgerCache. It holds the locations
* (entrylogfile, offset) for entry ids.
*/
public class LedgerEntryPage {
private final int pageSize;
private final int entriesPerPage;
private long ledger = -1;
private long firstEntry = BookieProtocol.INVALID_ENTRY_ID;
private final ByteBuffer page;
private boolean clean = true;
private boolean pinned = false;
private int useCount;
private int version;
public LedgerEntryPage(int pageSize, int entriesPerPage) {
this.pageSize = pageSize;
this.entriesPerPage = entriesPerPage;
page = ByteBuffer.allocateDirect(pageSize);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getLedger());
sb.append('@');
sb.append(getFirstEntry());
sb.append(clean ? " clean " : " dirty ");
sb.append(useCount);
return sb.toString();
}
synchronized public void usePage() {
useCount++;
}
synchronized public void pin() {
pinned = true;
}
synchronized public void unpin() {
pinned = false;
}
synchronized public boolean isPinned() {
return pinned;
}
synchronized public void releasePage() {
useCount--;
if (useCount < 0) {
throw new IllegalStateException("Use count has gone below 0");
}
}
synchronized private void checkPage() {
if (useCount <= 0) {
throw new IllegalStateException("Page not marked in use");
}
}
@Override
public boolean equals(Object other) {
if (other instanceof LedgerEntryPage) {
LedgerEntryPage otherLEP = (LedgerEntryPage) other;
return otherLEP.getLedger() == getLedger() && otherLEP.getFirstEntry() == getFirstEntry();
} else {
return false;
}
}
@Override
public int hashCode() {
return (int)getLedger() ^ (int)(getFirstEntry());
}
void setClean(int versionOfCleaning) {
this.clean = (versionOfCleaning == version);
}
boolean isClean() {
return clean;
}
public void setOffset(long offset, int position) {
checkPage();
version++;
this.clean = false;
page.putLong(position, offset);
}
public long getOffset(int position) {
checkPage();
return page.getLong(position);
}
static final byte zeroPage[] = new byte[64*1024];
public void zeroPage() {
checkPage();
page.clear();
page.put(zeroPage, 0, page.remaining());
clean = true;
}
public void readPage(FileInfo fi) throws IOException {
checkPage();
page.clear();
while(page.remaining() != 0) {
if (fi.read(page, getFirstEntry()*8) <= 0) {
throw new IOException("Short page read of ledger " + getLedger() + " tried to get " + page.capacity() + " from position " + getFirstEntry()*8 + " still need " + page.remaining());
}
}
clean = true;
}
public ByteBuffer getPageToWrite() {
checkPage();
page.clear();
return page;
}
void setLedger(long ledger) {
this.ledger = ledger;
}
long getLedger() {
return ledger;
}
int getVersion() {
return version;
}
void setFirstEntry(long firstEntry) {
if (firstEntry % entriesPerPage != 0) {
throw new IllegalArgumentException(firstEntry + " is not a multiple of " + entriesPerPage);
}
this.firstEntry = firstEntry;
}
long getFirstEntry() {
return firstEntry;
}
public boolean inUse() {
return useCount > 0;
}
public long getLastEntry() {
for(int i = entriesPerPage - 1; i >= 0; i--) {
if (getOffset(i*8) > 0) {
return i + firstEntry;
}
}
return 0;
}
}
LedgerStorage.java 0000664 0000000 0000000 00000005751 12445073612 0034310 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /*
*
* 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.bookie;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.apache.bookkeeper.jmx.BKMBeanInfo;
/**
* Interface for storing ledger data
* on persistant storage.
*/
interface LedgerStorage {
/**
* Start any background threads
* belonging to the storage system. For example,
* garbage collection.
*/
void start();
/**
* Cleanup and free any resources
* being used by the storage system.
*/
void shutdown() throws InterruptedException;
/**
* Whether a ledger exists
*/
boolean ledgerExists(long ledgerId) throws IOException;
/**
* Fenced the ledger id in ledger storage.
*
* @param ledgerId
* Ledger Id.
* @throws IOException when failed to fence the ledger.
*/
boolean setFenced(long ledgerId) throws IOException;
/**
* Check whether the ledger is fenced in ledger storage or not.
*
* @param ledgerId
* Ledger ID.
* @throws IOException
*/
boolean isFenced(long ledgerId) throws IOException;
/**
* Set the master key for a ledger
*/
void setMasterKey(long ledgerId, byte[] masterKey) throws IOException;
/**
* Get the master key for a ledger
* @throws IOException if there is an error reading the from the ledger
* @throws BookieException if no such ledger exists
*/
byte[] readMasterKey(long ledgerId) throws IOException, BookieException;
/**
* Add an entry to the storage.
* @return the entry id of the entry added
*/
long addEntry(ByteBuffer entry) throws IOException;
/**
* Read an entry from storage
*/
ByteBuffer getEntry(long ledgerId, long entryId) throws IOException;
/**
* Whether there is data in the storage which needs to be flushed
*/
boolean isFlushRequired();
/**
* Flushes all data in the storage. Once this is called,
* add data written to the LedgerStorage up until this point
* has been persisted to perminant storage
*/
void flush() throws IOException;
/**
* Get the JMX management bean for this LedgerStorage
*/
BKMBeanInfo getJMXBean();
}
MarkerFileChannel.java 0000664 0000000 0000000 00000007602 12445073612 0035070 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /*
*
* 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.bookie;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
/**
* This class is just a stub that can be used in collections with
* FileChannels
*/
public class MarkerFileChannel extends FileChannel {
@Override
public void force(boolean metaData) throws IOException {
// TODO Auto-generated method stub
}
@Override
public FileLock lock(long position, long size, boolean shared)
throws IOException {
// TODO Auto-generated method stub
return null;
}
@Override
public MappedByteBuffer map(MapMode mode, long position, long size)
throws IOException {
// TODO Auto-generated method stub
return null;
}
@Override
public long position() throws IOException {
// TODO Auto-generated method stub
return 0;
}
@Override
public FileChannel position(long newPosition) throws IOException {
// TODO Auto-generated method stub
return null;
}
@Override
public int read(ByteBuffer dst) throws IOException {
// TODO Auto-generated method stub
return 0;
}
@Override
public int read(ByteBuffer dst, long position) throws IOException {
// TODO Auto-generated method stub
return 0;
}
@Override
public long read(ByteBuffer[] dsts, int offset, int length)
throws IOException {
// TODO Auto-generated method stub
return 0;
}
@Override
public long size() throws IOException {
// TODO Auto-generated method stub
return 0;
}
@Override
public long transferFrom(ReadableByteChannel src, long position, long count)
throws IOException {
// TODO Auto-generated method stub
return 0;
}
@Override
public long transferTo(long position, long count, WritableByteChannel target)
throws IOException {
// TODO Auto-generated method stub
return 0;
}
@Override
public FileChannel truncate(long size) throws IOException {
// TODO Auto-generated method stub
return null;
}
@Override
public FileLock tryLock(long position, long size, boolean shared)
throws IOException {
// TODO Auto-generated method stub
return null;
}
@Override
public int write(ByteBuffer src) throws IOException {
// TODO Auto-generated method stub
return 0;
}
@Override
public int write(ByteBuffer src, long position) throws IOException {
// TODO Auto-generated method stub
return 0;
}
@Override
public long write(ByteBuffer[] srcs, int offset, int length)
throws IOException {
// TODO Auto-generated method stub
return 0;
}
@Override
protected void implCloseChannel() throws IOException {
// TODO Auto-generated method stub
}
}
ReadOnlyEntryLogger.java 0000664 0000000 0000000 00000003416 12445073612 0035454 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /*
*
* 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.bookie;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.apache.bookkeeper.conf.ServerConfiguration;
/**
* Read Only Entry Logger
*/
public class ReadOnlyEntryLogger extends EntryLogger {
public ReadOnlyEntryLogger(ServerConfiguration conf) throws IOException {
super(conf, new LedgerDirsManager(conf));
}
@Override
protected void initialize() throws IOException {
// do nothing for read only entry logger
}
@Override
void createNewLog() throws IOException {
throw new IOException("Can't create new entry log using a readonly entry logger.");
}
@Override
protected boolean removeEntryLog(long entryLogId) {
// can't remove entry log in readonly mode
return false;
}
@Override
synchronized long addEntry(long ledger, ByteBuffer entry) throws IOException {
throw new IOException("Can't add entry to a readonly entry logger.");
}
}
ReadOnlyFileInfo.java 0000664 0000000 0000000 00000002452 12445073612 0034705 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /*
*
* 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.bookie;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.BufferUnderflowException;
import java.nio.channels.FileChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Provide a readonly file info.
*/
class ReadOnlyFileInfo extends FileInfo {
public ReadOnlyFileInfo(File lf, byte[] masterKey) throws IOException {
super(lf, masterKey);
mode = "r";
}
}
ScanAndCompareGarbageCollector.java 0000664 0000000 0000000 00000010172 12445073612 0037510 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie /**
*
* 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.bookie;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
import org.apache.bookkeeper.meta.LedgerManager;
import org.apache.bookkeeper.meta.LedgerManager.LedgerRange;
import org.apache.bookkeeper.meta.LedgerManager.LedgerRangeIterator;
import org.apache.bookkeeper.util.SnapshotMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Garbage collector implementation using scan and compare.
*
*
* Garbage collection is processed as below:
*
* - fetch all existing ledgers from zookeeper or metastore according to
* the LedgerManager, called globalActiveLedgers
*
- fetch all active ledgers from bookie server, said bkActiveLedgers
*
- loop over bkActiveLedgers to find those ledgers that are not in
* globalActiveLedgers, do garbage collection on them.
*
*
*/
public class ScanAndCompareGarbageCollector implements GarbageCollector{
static final Logger LOG = LoggerFactory.getLogger(ScanAndCompareGarbageCollector.class);
private SnapshotMap activeLedgers;
private LedgerManager ledgerManager;
public ScanAndCompareGarbageCollector(LedgerManager ledgerManager, SnapshotMap activeLedgers) {
this.ledgerManager = ledgerManager;
this.activeLedgers = activeLedgers;
}
@Override
public void gc(GarbageCleaner garbageCleaner) {
// create a snapshot first
NavigableMap bkActiveLedgersSnapshot =
this.activeLedgers.snapshot();
LedgerRangeIterator ledgerRangeIterator = ledgerManager.getLedgerRanges();
try {
// Empty global active ledgers, need to remove all local active ledgers.
if (!ledgerRangeIterator.hasNext()) {
for (Long bkLid : bkActiveLedgersSnapshot.keySet()) {
// remove it from current active ledger
bkActiveLedgersSnapshot.remove(bkLid);
garbageCleaner.clean(bkLid);
}
}
long lastEnd = -1;
while(ledgerRangeIterator.hasNext()) {
LedgerRange lRange = ledgerRangeIterator.next();
Map subBkActiveLedgers = null;
Long start = lastEnd + 1;
Long end = lRange.end();
if (!ledgerRangeIterator.hasNext()) {
end = Long.MAX_VALUE;
}
subBkActiveLedgers = bkActiveLedgersSnapshot.subMap(
start, true, end, true);
Set ledgersInMetadata = lRange.getLedgers();
LOG.debug("Active in metadata {}, Active in bookie {}",
ledgersInMetadata, subBkActiveLedgers.keySet());
for (Long bkLid : subBkActiveLedgers.keySet()) {
if (!ledgersInMetadata.contains(bkLid)) {
// remove it from current active ledger
subBkActiveLedgers.remove(bkLid);
garbageCleaner.clean(bkLid);
}
}
lastEnd = end;
}
} catch (Exception e) {
// ignore exception, collecting garbage next time
LOG.warn("Exception when iterating over the metadata {}", e);
}
}
}
bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/client/ 0000775 0000000 0000000 00000000000 12445073612 0030773 5 ustar 00root root 0000000 0000000 AsyncCallback.java 0000664 0000000 0000000 00000010337 12445073612 0034255 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/client package org.apache.bookkeeper.client;
import java.util.Enumeration;
/**
* 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.
*/
public interface AsyncCallback {
public interface AddCallback {
/**
* Callback declaration
*
* @param rc
* return code
* @param lh
* ledger handle
* @param entryId
* entry identifier
* @param ctx
* context object
*/
void addComplete(int rc, LedgerHandle lh, long entryId, Object ctx);
}
public interface CloseCallback {
/**
* Callback definition
*
* @param rc
* return code
* @param lh
* ledger handle
* @param ctx
* context object
*/
void closeComplete(int rc, LedgerHandle lh, Object ctx);
}
public interface CreateCallback {
/**
* Declaration of callback method
*
* @param rc
* return status
* @param lh
* ledger handle
* @param ctx
* context object
*/
void createComplete(int rc, LedgerHandle lh, Object ctx);
}
public interface OpenCallback {
/**
* Callback for asynchronous call to open ledger
*
* @param rc
* Return code
* @param lh
* ledger handle
* @param ctx
* context object
*/
public void openComplete(int rc, LedgerHandle lh, Object ctx);
}
public interface ReadCallback {
/**
* Callback declaration
*
* @param rc
* return code
* @param lh
* ledger handle
* @param seq
* sequence of entries
* @param ctx
* context object
*/
void readComplete(int rc, LedgerHandle lh, Enumeration seq,
Object ctx);
}
public interface DeleteCallback {
/**
* Callback definition for delete operations
*
* @param rc
* return code
* @param ctx
* context object
*/
void deleteComplete(int rc, Object ctx);
}
public interface ReadLastConfirmedCallback {
/**
* Callback definition for bookie recover operations
*
* @param rc Return code
* @param lastConfirmed The entry id of the last confirmed write or
* {@link LedgerHandle#INVALID_ENTRY_ID INVALID_ENTRY_ID}
* if no entry has been confirmed
* @param ctx
* context object
*/
void readLastConfirmedComplete(int rc, long lastConfirmed, Object ctx);
}
public interface RecoverCallback {
/**
* Callback definition for bookie recover operations
*
* @param rc
* return code
* @param ctx
* context object
*/
void recoverComplete(int rc, Object ctx);
}
public interface IsClosedCallback {
/**
* Callback definition for isClosed operation
*
* @param rc
* return code
* @param isClosed
* true if ledger is closed
*/
void isClosedComplete(int rc, boolean isClosed, Object ctx);
}
}
BKException.java 0000664 0000000 0000000 00000027552 12445073612 0033745 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/client 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.lang.Exception;
/**
* Class the enumerates all the possible error conditions
*
*/
@SuppressWarnings("serial")
public abstract class BKException extends Exception {
private int code;
BKException(int code) {
this.code = code;
}
/**
* Create an exception from an error code
* @param code return error code
* @return correponding exception
*/
public static BKException create(int code) {
switch (code) {
case Code.ReadException:
return new BKReadException();
case Code.QuorumException:
return new BKQuorumException();
case Code.NoBookieAvailableException:
return new BKBookieException();
case Code.DigestNotInitializedException:
return new BKDigestNotInitializedException();
case Code.DigestMatchException:
return new BKDigestMatchException();
case Code.NotEnoughBookiesException:
return new BKNotEnoughBookiesException();
case Code.NoSuchLedgerExistsException:
return new BKNoSuchLedgerExistsException();
case Code.BookieHandleNotAvailableException:
return new BKBookieHandleNotAvailableException();
case Code.ZKException:
return new ZKException();
case Code.MetaStoreException:
return new MetaStoreException();
case Code.LedgerRecoveryException:
return new BKLedgerRecoveryException();
case Code.LedgerClosedException:
return new BKLedgerClosedException();
case Code.WriteException:
return new BKWriteException();
case Code.NoSuchEntryException:
return new BKNoSuchEntryException();
case Code.IncorrectParameterException:
return new BKIncorrectParameterException();
case Code.InterruptedException:
return new BKInterruptedException();
case Code.ProtocolVersionException:
return new BKProtocolVersionException();
case Code.MetadataVersionException:
return new BKMetadataVersionException();
case Code.LedgerFencedException:
return new BKLedgerFencedException();
case Code.UnauthorizedAccessException:
return new BKUnauthorizedAccessException();
case Code.UnclosedFragmentException:
return new BKUnclosedFragmentException();
case Code.WriteOnReadOnlyBookieException:
return new BKWriteOnReadOnlyBookieException();
case Code.ReplicationException:
return new BKReplicationException();
case Code.IllegalOpException:
return new BKIllegalOpException();
default:
return new BKUnexpectedConditionException();
}
}
/**
* List of return codes
*
*/
public interface Code {
int OK = 0;
int ReadException = -1;
int QuorumException = -2;
int NoBookieAvailableException = -3;
int DigestNotInitializedException = -4;
int DigestMatchException = -5;
int NotEnoughBookiesException = -6;
int NoSuchLedgerExistsException = -7;
int BookieHandleNotAvailableException = -8;
int ZKException = -9;
int LedgerRecoveryException = -10;
int LedgerClosedException = -11;
int WriteException = -12;
int NoSuchEntryException = -13;
int IncorrectParameterException = -14;
int InterruptedException = -15;
int ProtocolVersionException = -16;
int MetadataVersionException = -17;
int MetaStoreException = -18;
int IllegalOpException = -100;
int LedgerFencedException = -101;
int UnauthorizedAccessException = -102;
int UnclosedFragmentException = -103;
int WriteOnReadOnlyBookieException = -104;
// generic exception code used to propagate in replication pipeline
int ReplicationException = -200;
// For all unexpected error conditions
int UnexpectedConditionException = -999;
}
public void setCode(int code) {
this.code = code;
}
public int getCode() {
return this.code;
}
public static String getMessage(int code) {
switch (code) {
case Code.OK:
return "No problem";
case Code.ReadException:
return "Error while reading ledger";
case Code.QuorumException:
return "Invalid quorum size on ensemble size";
case Code.NoBookieAvailableException:
return "Invalid quorum size on ensemble size";
case Code.DigestNotInitializedException:
return "Digest engine not initialized";
case Code.DigestMatchException:
return "Entry digest does not match";
case Code.NotEnoughBookiesException:
return "Not enough non-faulty bookies available";
case Code.NoSuchLedgerExistsException:
return "No such ledger exists";
case Code.BookieHandleNotAvailableException:
return "Bookie handle is not available";
case Code.ZKException:
return "Error while using ZooKeeper";
case Code.MetaStoreException:
return "Error while using MetaStore";
case Code.LedgerRecoveryException:
return "Error while recovering ledger";
case Code.LedgerClosedException:
return "Attempt to write to a closed ledger";
case Code.WriteException:
return "Write failed on bookie";
case Code.NoSuchEntryException:
return "No such entry";
case Code.IncorrectParameterException:
return "Incorrect parameter input";
case Code.InterruptedException:
return "Interrupted while waiting for permit";
case Code.ProtocolVersionException:
return "Bookie protocol version on server is incompatible with client";
case Code.MetadataVersionException:
return "Bad ledger metadata version";
case Code.LedgerFencedException:
return "Ledger has been fenced off. Some other client must have opened it to read";
case Code.UnauthorizedAccessException:
return "Attempted to access ledger using the wrong password";
case Code.UnclosedFragmentException:
return "Attempting to use an unclosed fragment; This is not safe";
case Code.WriteOnReadOnlyBookieException:
return "Attempting to write on ReadOnly bookie";
case Code.ReplicationException:
return "Errors in replication pipeline";
case Code.IllegalOpException:
return "Invalid operation";
default:
return "Unexpected condition";
}
}
public static class BKReadException extends BKException {
public BKReadException() {
super(Code.ReadException);
}
}
public static class BKNoSuchEntryException extends BKException {
public BKNoSuchEntryException() {
super(Code.NoSuchEntryException);
}
}
public static class BKQuorumException extends BKException {
public BKQuorumException() {
super(Code.QuorumException);
}
}
public static class BKBookieException extends BKException {
public BKBookieException() {
super(Code.NoBookieAvailableException);
}
}
public static class BKDigestNotInitializedException extends BKException {
public BKDigestNotInitializedException() {
super(Code.DigestNotInitializedException);
}
}
public static class BKDigestMatchException extends BKException {
public BKDigestMatchException() {
super(Code.DigestMatchException);
}
}
public static class BKIllegalOpException extends BKException {
public BKIllegalOpException() {
super(Code.IllegalOpException);
}
}
public static class BKUnexpectedConditionException extends BKException {
public BKUnexpectedConditionException() {
super(Code.UnexpectedConditionException);
}
}
public static class BKNotEnoughBookiesException extends BKException {
public BKNotEnoughBookiesException() {
super(Code.NotEnoughBookiesException);
}
}
public static class BKWriteException extends BKException {
public BKWriteException() {
super(Code.WriteException);
}
}
public static class BKProtocolVersionException extends BKException {
public BKProtocolVersionException() {
super(Code.ProtocolVersionException);
}
}
public static class BKMetadataVersionException extends BKException {
public BKMetadataVersionException() {
super(Code.MetadataVersionException);
}
}
public static class BKNoSuchLedgerExistsException extends BKException {
public BKNoSuchLedgerExistsException() {
super(Code.NoSuchLedgerExistsException);
}
}
public static class BKBookieHandleNotAvailableException extends BKException {
public BKBookieHandleNotAvailableException() {
super(Code.BookieHandleNotAvailableException);
}
}
public static class ZKException extends BKException {
public ZKException() {
super(Code.ZKException);
}
}
public static class MetaStoreException extends BKException {
public MetaStoreException() {
super(Code.MetaStoreException);
}
}
public static class BKLedgerRecoveryException extends BKException {
public BKLedgerRecoveryException() {
super(Code.LedgerRecoveryException);
}
}
public static class BKLedgerClosedException extends BKException {
public BKLedgerClosedException() {
super(Code.LedgerClosedException);
}
}
public static class BKIncorrectParameterException extends BKException {
public BKIncorrectParameterException() {
super(Code.IncorrectParameterException);
}
}
public static class BKInterruptedException extends BKException {
public BKInterruptedException() {
super(Code.InterruptedException);
}
}
public static class BKLedgerFencedException extends BKException {
public BKLedgerFencedException() {
super(Code.LedgerFencedException);
}
}
public static class BKUnauthorizedAccessException extends BKException {
public BKUnauthorizedAccessException() {
super(Code.UnauthorizedAccessException);
}
}
public static class BKUnclosedFragmentException extends BKException {
public BKUnclosedFragmentException() {
super(Code.UnclosedFragmentException);
}
}
public static class BKWriteOnReadOnlyBookieException extends BKException {
public BKWriteOnReadOnlyBookieException() {
super(Code.WriteOnReadOnlyBookieException);
}
}
public static class BKReplicationException extends BKException {
public BKReplicationException() {
super(Code.ReplicationException);
}
}
}
BookKeeper.java 0000664 0000000 0000000 00000062741 12445073612 0033617 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/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.
*
*/
package org.apache.bookkeeper.client;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.apache.bookkeeper.client.AsyncCallback.CreateCallback;
import org.apache.bookkeeper.client.AsyncCallback.DeleteCallback;
import org.apache.bookkeeper.client.AsyncCallback.OpenCallback;
import org.apache.bookkeeper.client.AsyncCallback.IsClosedCallback;
import org.apache.bookkeeper.client.BKException.Code;
import org.apache.bookkeeper.conf.ClientConfiguration;
import org.apache.bookkeeper.meta.LedgerManager;
import org.apache.bookkeeper.meta.LedgerManagerFactory;
import org.apache.bookkeeper.proto.BookieClient;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.GenericCallback;
import org.apache.bookkeeper.util.OrderedSafeExecutor;
import org.apache.bookkeeper.util.ZkUtils;
import org.apache.bookkeeper.zookeeper.ZooKeeperWatcherBase;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooKeeper;
import org.jboss.netty.channel.socket.ClientSocketChannelFactory;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* BookKeeper client. We assume there is one single writer to a ledger at any
* time.
*
* There are four possible operations: start a new ledger, write to a ledger,
* read from a ledger and delete a ledger.
*
* The exceptions resulting from synchronous calls and error code resulting from
* asynchronous calls can be found in the class {@link BKException}.
*
*
*/
public class BookKeeper {
static final Logger LOG = LoggerFactory.getLogger(BookKeeper.class);
final ZooKeeper zk;
final CountDownLatch connectLatch = new CountDownLatch(1);
final static int zkConnectTimeoutMs = 5000;
final ClientSocketChannelFactory channelFactory;
// whether the socket factory is one we created, or is owned by whoever
// instantiated us
boolean ownChannelFactory = false;
// whether the zk handle is one we created, or is owned by whoever
// instantiated us
boolean ownZKHandle = false;
final BookieClient bookieClient;
final BookieWatcher bookieWatcher;
final OrderedSafeExecutor mainWorkerPool;
final ScheduledExecutorService scheduler;
// Ledger manager responsible for how to store ledger meta data
final LedgerManagerFactory ledgerManagerFactory;
final LedgerManager ledgerManager;
final ClientConfiguration conf;
interface ZKConnectCallback {
public void connected();
public void connectionFailed(int code);
}
/**
* Create a bookkeeper client. A zookeeper client and a client socket factory
* will be instantiated as part of this constructor.
*
* @param servers
* A list of one of more servers on which zookeeper is running. The
* client assumes that the running bookies have been registered with
* zookeeper under the path
* {@link BookieWatcher#bookieRegistrationPath}
* @throws IOException
* @throws InterruptedException
* @throws KeeperException
*/
public BookKeeper(String servers) throws IOException, InterruptedException,
KeeperException {
this(new ClientConfiguration().setZkServers(servers));
}
/**
* Create a bookkeeper client using a configuration object.
* A zookeeper client and a client socket factory will be
* instantiated as part of this constructor.
*
* @param conf
* Client Configuration object
* @throws IOException
* @throws InterruptedException
* @throws KeeperException
*/
public BookKeeper(final ClientConfiguration conf)
throws IOException, InterruptedException, KeeperException {
this.conf = conf;
ZooKeeperWatcherBase w = new ZooKeeperWatcherBase(conf.getZkTimeout());
this.zk = ZkUtils
.createConnectedZookeeperClient(conf.getZkServers(), w);
this.channelFactory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
this.scheduler = Executors.newSingleThreadScheduledExecutor();
mainWorkerPool = new OrderedSafeExecutor(conf.getNumWorkerThreads());
bookieClient = new BookieClient(conf, channelFactory, mainWorkerPool);
bookieWatcher = new BookieWatcher(conf, scheduler, this);
bookieWatcher.readBookiesBlocking();
ledgerManagerFactory = LedgerManagerFactory.newLedgerManagerFactory(conf, zk);
ledgerManager = ledgerManagerFactory.newLedgerManager();
ownChannelFactory = true;
ownZKHandle = true;
}
/**
* Create a bookkeeper client but use the passed in zookeeper client instead
* of instantiating one.
*
* @param conf
* Client Configuration object
* {@link ClientConfiguration}
* @param zk
* Zookeeper client instance connected to the zookeeper with which
* the bookies have registered
* @throws IOException
* @throws InterruptedException
* @throws KeeperException
*/
public BookKeeper(ClientConfiguration conf, ZooKeeper zk)
throws IOException, InterruptedException, KeeperException {
this(conf, zk, new NioClientSocketChannelFactory(Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
ownChannelFactory = true;
}
/**
* Create a bookkeeper client but use the passed in zookeeper client and
* client socket channel factory instead of instantiating those.
*
* @param conf
* Client Configuration Object
* {@link ClientConfiguration}
* @param zk
* Zookeeper client instance connected to the zookeeper with which
* the bookies have registered. The ZooKeeper client must be connected
* before it is passed to BookKeeper. Otherwise a KeeperException is thrown.
* @param channelFactory
* A factory that will be used to create connections to the bookies
* @throws IOException
* @throws InterruptedException
* @throws KeeperException if the passed zk handle is not connected
*/
public BookKeeper(ClientConfiguration conf, ZooKeeper zk, ClientSocketChannelFactory channelFactory)
throws IOException, InterruptedException, KeeperException {
if (zk == null || channelFactory == null) {
throw new NullPointerException();
}
if (!zk.getState().isConnected()) {
LOG.error("Unconnected zookeeper handle passed to bookkeeper");
throw KeeperException.create(KeeperException.Code.CONNECTIONLOSS);
}
this.conf = conf;
this.zk = zk;
this.channelFactory = channelFactory;
this.scheduler = Executors.newSingleThreadScheduledExecutor();
mainWorkerPool = new OrderedSafeExecutor(conf.getNumWorkerThreads());
bookieClient = new BookieClient(conf, channelFactory, mainWorkerPool);
bookieWatcher = new BookieWatcher(conf, scheduler, this);
bookieWatcher.readBookiesBlocking();
ledgerManagerFactory = LedgerManagerFactory.newLedgerManagerFactory(conf, zk);
ledgerManager = ledgerManagerFactory.newLedgerManager();
}
LedgerManager getLedgerManager() {
return ledgerManager;
}
/**
* There are 2 digest types that can be used for verification. The CRC32 is
* cheap to compute but does not protect against byzantine bookies (i.e., a
* bookie might report fake bytes and a matching CRC32). The MAC code is more
* expensive to compute, but is protected by a password, i.e., a bookie can't
* report fake bytes with a mathching MAC unless it knows the password
*/
public enum DigestType {
MAC, CRC32
};
ZooKeeper getZkHandle() {
return zk;
}
protected ClientConfiguration getConf() {
return conf;
}
/**
* Get the BookieClient, currently used for doing bookie recovery.
*
* @return BookieClient for the BookKeeper instance.
*/
BookieClient getBookieClient() {
return bookieClient;
}
/**
* Creates a new ledger asynchronously. To create a ledger, we need to specify
* the ensemble size, the quorum size, the digest type, a password, a callback
* implementation, and an optional control object. The ensemble size is how
* many bookies the entries should be striped among and the quorum size is the
* degree of replication of each entry. The digest type is either a MAC or a
* CRC. Note that the CRC option is not able to protect a client against a
* bookie that replaces an entry. The password is used not only to
* authenticate access to a ledger, but also to verify entries in ledgers.
*
* @param ensSize
* number of bookies over which to stripe entries
* @param writeQuorumSize
* number of bookies each entry will be written to. each of these bookies
* must acknowledge the entry before the call is completed.
* @param digestType
* digest type, either MAC or CRC32
* @param passwd
* password
* @param cb
* createCallback implementation
* @param ctx
* optional control object
*/
public void asyncCreateLedger(final int ensSize,
final int writeQuorumSize,
final DigestType digestType,
final byte[] passwd, final CreateCallback cb, final Object ctx)
{
asyncCreateLedger(ensSize, writeQuorumSize, writeQuorumSize, digestType, passwd, cb, ctx);
}
/**
* Creates a new ledger asynchronously. Ledgers created with this call have
* a separate write quorum and ack quorum size. The write quorum must be larger than
* the ack quorum.
*
* Separating the write and the ack quorum allows the BookKeeper client to continue
* writing when a bookie has failed but the failure has not yet been detected. Detecting
* a bookie has failed can take a number of seconds, as configured by the read timeout
* {@link ClientConfiguration#getReadTimeout()}. Once the bookie failure is detected,
* that bookie will be removed from the ensemble.
*
* The other parameters match those of {@link #asyncCreateLedger(int, int, DigestType, byte[],
* AsyncCallback.CreateCallback, Object)}
*
* @param ensSize
* number of bookies over which to stripe entries
* @param writeQuorumSize
* number of bookies each entry will be written to
* @param ackQuorumSize
* number of bookies which must acknowledge an entry before the call is completed
* @param digestType
* digest type, either MAC or CRC32
* @param passwd
* password
* @param cb
* createCallback implementation
* @param ctx
* optional control object
*/
public void asyncCreateLedger(final int ensSize,
final int writeQuorumSize,
final int ackQuorumSize,
final DigestType digestType,
final byte[] passwd, final CreateCallback cb, final Object ctx) {
if (writeQuorumSize < ackQuorumSize) {
throw new IllegalArgumentException("Write quorum must be larger than ack quorum");
}
new LedgerCreateOp(BookKeeper.this, ensSize, writeQuorumSize,
ackQuorumSize, digestType, passwd, cb, ctx)
.initiate();
}
/**
* Creates a new ledger. Default of 3 servers, and quorum of 2 servers.
*
* @param digestType
* digest type, either MAC or CRC32
* @param passwd
* password
* @return a handle to the newly created ledger
* @throws InterruptedException
* @throws BKException
*/
public LedgerHandle createLedger(DigestType digestType, byte passwd[])
throws BKException, InterruptedException {
return createLedger(3, 2, digestType, passwd);
}
/**
* Synchronous call to create ledger. Parameters match those of
* {@link #asyncCreateLedger(int, int, DigestType, byte[],
* AsyncCallback.CreateCallback, Object)}
*
* @param ensSize
* @param qSize
* @param digestType
* @param passwd
* @return a handle to the newly created ledger
* @throws InterruptedException
* @throws BKException
*/
public LedgerHandle createLedger(int ensSize, int qSize,
DigestType digestType, byte passwd[])
throws InterruptedException, BKException {
return createLedger(ensSize, qSize, qSize, digestType, passwd);
}
/**
* Synchronous call to create ledger. Parameters match those of
* {@link #asyncCreateLedger(int, int, int, DigestType, byte[],
* AsyncCallback.CreateCallback, Object)}
*
* @param ensSize
* @param writeQuorumSize
* @param ackQuorumSize
* @param digestType
* @param passwd
* @return a handle to the newly created ledger
* @throws InterruptedException
* @throws BKException
*/
public LedgerHandle createLedger(int ensSize, int writeQuorumSize, int ackQuorumSize,
DigestType digestType, byte passwd[])
throws InterruptedException, BKException {
SyncCounter counter = new SyncCounter();
counter.inc();
/*
* Calls asynchronous version
*/
asyncCreateLedger(ensSize, writeQuorumSize, ackQuorumSize, digestType, passwd,
new SyncCreateCallback(), counter);
/*
* Wait
*/
counter.block(0);
if (counter.getrc() != BKException.Code.OK) {
LOG.error("Error while creating ledger : {}", counter.getrc());
throw BKException.create(counter.getrc());
} else if (counter.getLh() == null) {
LOG.error("Unexpected condition : no ledger handle returned for a success ledger creation");
throw BKException.create(BKException.Code.UnexpectedConditionException);
}
return counter.getLh();
}
/**
* Open existing ledger asynchronously for reading.
*
* Opening a ledger with this method invokes fencing and recovery on the ledger
* if the ledger has not been closed. Fencing will block all other clients from
* writing to the ledger. Recovery will make sure that the ledger is closed
* before reading from it.
*
* Recovery also makes sure that any entries which reached one bookie, but not a
* quorum, will be replicated to a quorum of bookies. This occurs in cases were
* the writer of a ledger crashes after sending a write request to one bookie but
* before being able to send it to the rest of the bookies in the quorum.
*
* If the ledger is already closed, neither fencing nor recovery will be applied.
*
* @see LedgerHandle#asyncClose
*
* @param lId
* ledger identifier
* @param digestType
* digest type, either MAC or CRC32
* @param passwd
* password
* @param ctx
* optional control object
*/
public void asyncOpenLedger(final long lId, final DigestType digestType, final byte passwd[],
final OpenCallback cb, final Object ctx) {
new LedgerOpenOp(BookKeeper.this, lId, digestType, passwd, cb, ctx).initiate();
}
/**
* Open existing ledger asynchronously for reading, but it does not try to
* recover the ledger if it is not yet closed. The application needs to use
* it carefully, since the writer might have crashed and ledger will remain
* unsealed forever if there is no external mechanism to detect the failure
* of the writer and the ledger is not open in a safe manner, invoking the
* recovery procedure.
*
* Opening a ledger without recovery does not fence the ledger. As such, other
* clients can continue to write to the ledger.
*
* This method returns a read only ledger handle. It will not be possible
* to add entries to the ledger. Any attempt to add entries will throw an
* exception.
*
* Reads from the returned ledger will only be able to read entries up until
* the lastConfirmedEntry at the point in time at which the ledger was opened.
*
* @param lId
* ledger identifier
* @param digestType
* digest type, either MAC or CRC32
* @param passwd
* password
* @param ctx
* optional control object
*/
public void asyncOpenLedgerNoRecovery(final long lId, final DigestType digestType, final byte passwd[],
final OpenCallback cb, final Object ctx) {
new LedgerOpenOp(BookKeeper.this, lId, digestType, passwd, cb, ctx).initiateWithoutRecovery();
}
/**
* Synchronous open ledger call
*
* @see #asyncOpenLedger
* @param lId
* ledger identifier
* @param digestType
* digest type, either MAC or CRC32
* @param passwd
* password
* @return a handle to the open ledger
* @throws InterruptedException
* @throws BKException
*/
public LedgerHandle openLedger(long lId, DigestType digestType, byte passwd[])
throws BKException, InterruptedException {
SyncCounter counter = new SyncCounter();
counter.inc();
/*
* Calls async open ledger
*/
asyncOpenLedger(lId, digestType, passwd, new SyncOpenCallback(), counter);
/*
* Wait
*/
counter.block(0);
if (counter.getrc() != BKException.Code.OK)
throw BKException.create(counter.getrc());
return counter.getLh();
}
/**
* Synchronous, unsafe open ledger call
*
* @see #asyncOpenLedgerNoRecovery
* @param lId
* ledger identifier
* @param digestType
* digest type, either MAC or CRC32
* @param passwd
* password
* @return a handle to the open ledger
* @throws InterruptedException
* @throws BKException
*/
public LedgerHandle openLedgerNoRecovery(long lId, DigestType digestType, byte passwd[])
throws BKException, InterruptedException {
SyncCounter counter = new SyncCounter();
counter.inc();
/*
* Calls async open ledger
*/
asyncOpenLedgerNoRecovery(lId, digestType, passwd,
new SyncOpenCallback(), counter);
/*
* Wait
*/
counter.block(0);
if (counter.getrc() != BKException.Code.OK)
throw BKException.create(counter.getrc());
return counter.getLh();
}
/**
* Deletes a ledger asynchronously.
*
* @param lId
* ledger Id
* @param cb
* deleteCallback implementation
* @param ctx
* optional control object
*/
public void asyncDeleteLedger(final long lId, final DeleteCallback cb, final Object ctx) {
new LedgerDeleteOp(BookKeeper.this, lId, cb, ctx).initiate();
}
/**
* Synchronous call to delete a ledger. Parameters match those of
* {@link #asyncDeleteLedger(long, AsyncCallback.DeleteCallback, Object)}
*
* @param lId
* ledgerId
* @throws InterruptedException
* @throws BKException.BKNoSuchLedgerExistsException if the ledger doesn't exist
* @throws BKException
*/
public void deleteLedger(long lId) throws InterruptedException, BKException {
SyncCounter counter = new SyncCounter();
counter.inc();
// Call asynchronous version
asyncDeleteLedger(lId, new SyncDeleteCallback(), counter);
// Wait
counter.block(0);
if (counter.getrc() != BKException.Code.OK) {
LOG.error("Error deleting ledger " + lId + " : " + counter.getrc());
throw BKException.create(counter.getrc());
}
}
/**
* Check asynchronously whether the ledger with identifier lId
* has been closed.
*
* @param lId ledger identifier
* @param cb callback method
*/
public void asyncIsClosed(long lId, final IsClosedCallback cb, final Object ctx){
ledgerManager.readLedgerMetadata(lId, new GenericCallback(){
public void operationComplete(int rc, LedgerMetadata lm){
if (rc == BKException.Code.OK) {
cb.isClosedComplete(rc, lm.isClosed(), ctx);
} else {
cb.isClosedComplete(rc, false, ctx);
}
}
});
}
/**
* Check whether the ledger with identifier lId
* has been closed.
*
* @param lId
* @return boolean true if ledger has been closed
* @throws BKException
*/
public boolean isClosed(long lId)
throws BKException, InterruptedException {
final class Result {
int rc;
boolean isClosed;
final CountDownLatch notifier = new CountDownLatch(1);
}
final Result result = new Result();
final IsClosedCallback cb = new IsClosedCallback(){
public void isClosedComplete(int rc, boolean isClosed, Object ctx){
result.isClosed = isClosed;
result.rc = rc;
result.notifier.countDown();
}
};
/*
* Call asynchronous version of isClosed
*/
asyncIsClosed(lId, cb, null);
/*
* Wait for callback
*/
result.notifier.await();
if (result.rc != BKException.Code.OK) {
throw BKException.create(result.rc);
}
return result.isClosed;
}
/**
* Shuts down client.
*
*/
public void close() throws InterruptedException, BKException {
scheduler.shutdown();
if (!scheduler.awaitTermination(10, TimeUnit.SECONDS)) {
LOG.warn("The scheduler did not shutdown cleanly");
}
mainWorkerPool.shutdown();
if (!mainWorkerPool.awaitTermination(10, TimeUnit.SECONDS)) {
LOG.warn("The mainWorkerPool did not shutdown cleanly");
}
bookieClient.close();
try {
ledgerManager.close();
ledgerManagerFactory.uninitialize();
} catch (IOException ie) {
LOG.error("Failed to close ledger manager : ", ie);
}
if (ownChannelFactory) {
channelFactory.releaseExternalResources();
}
if (ownZKHandle) {
zk.close();
}
}
private static class SyncCreateCallback implements CreateCallback {
/**
* Create callback implementation for synchronous create call.
*
* @param rc
* return code
* @param lh
* ledger handle object
* @param ctx
* optional control object
*/
public void createComplete(int rc, LedgerHandle lh, Object ctx) {
SyncCounter counter = (SyncCounter) ctx;
counter.setLh(lh);
counter.setrc(rc);
counter.dec();
}
}
static class SyncOpenCallback implements OpenCallback {
/**
* Callback method for synchronous open operation
*
* @param rc
* return code
* @param lh
* ledger handle
* @param ctx
* optional control object
*/
public void openComplete(int rc, LedgerHandle lh, Object ctx) {
SyncCounter counter = (SyncCounter) ctx;
counter.setLh(lh);
LOG.debug("Open complete: {}", rc);
counter.setrc(rc);
counter.dec();
}
}
private static class SyncDeleteCallback implements DeleteCallback {
/**
* Delete callback implementation for synchronous delete call.
*
* @param rc
* return code
* @param ctx
* optional control object
*/
public void deleteComplete(int rc, Object ctx) {
SyncCounter counter = (SyncCounter) ctx;
counter.setrc(rc);
counter.dec();
}
}
}
BookKeeperAdmin.java 0000664 0000000 0000000 00000112570 12445073612 0034564 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/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.
*
*/
package org.apache.bookkeeper.client;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.UUID;
import org.apache.bookkeeper.client.AsyncCallback.OpenCallback;
import org.apache.bookkeeper.client.AsyncCallback.RecoverCallback;
import org.apache.bookkeeper.client.BookKeeper.SyncOpenCallback;
import org.apache.bookkeeper.client.LedgerFragmentReplicator.SingleFragmentCallback;
import org.apache.bookkeeper.conf.ClientConfiguration;
import org.apache.bookkeeper.meta.LedgerManager.LedgerRange;
import org.apache.bookkeeper.meta.LedgerManager.LedgerRangeIterator;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.MultiCallback;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.Processor;
import org.apache.bookkeeper.util.BookKeeperConstants;
import org.apache.bookkeeper.util.IOUtils;
import org.apache.bookkeeper.util.ZkUtils;
import org.apache.bookkeeper.zookeeper.ZooKeeperWatcherBase;
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZKUtil;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.KeeperException.Code;
import org.apache.zookeeper.ZooDefs.Ids;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Admin client for BookKeeper clusters
*/
public class BookKeeperAdmin {
private static Logger LOG = LoggerFactory.getLogger(BookKeeperAdmin.class);
// ZK client instance
private ZooKeeper zk;
// ZK ledgers related String constants
private final String bookiesPath;
// BookKeeper client instance
private BookKeeper bkc;
// LedgerFragmentReplicator instance
private LedgerFragmentReplicator lfr;
/*
* Random number generator used to choose an available bookie server to
* replicate data from a dead bookie.
*/
private Random rand = new Random();
/**
* Constructor that takes in a ZooKeeper servers connect string so we know
* how to connect to ZooKeeper to retrieve information about the BookKeeper
* cluster. We need this before we can do any type of admin operations on
* the BookKeeper cluster.
*
* @param zkServers
* Comma separated list of hostname:port pairs for the ZooKeeper
* servers cluster.
* @throws IOException
* throws this exception if there is an error instantiating the
* ZooKeeper client.
* @throws InterruptedException
* Throws this exception if there is an error instantiating the
* BookKeeper client.
* @throws KeeperException
* Throws this exception if there is an error instantiating the
* BookKeeper client.
*/
public BookKeeperAdmin(String zkServers) throws IOException, InterruptedException, KeeperException {
this(new ClientConfiguration().setZkServers(zkServers));
}
/**
* Constructor that takes in a configuration object so we know
* how to connect to ZooKeeper to retrieve information about the BookKeeper
* cluster. We need this before we can do any type of admin operations on
* the BookKeeper cluster.
*
* @param conf
* Client Configuration Object
* @throws IOException
* throws this exception if there is an error instantiating the
* ZooKeeper client.
* @throws InterruptedException
* Throws this exception if there is an error instantiating the
* BookKeeper client.
* @throws KeeperException
* Throws this exception if there is an error instantiating the
* BookKeeper client.
*/
public BookKeeperAdmin(ClientConfiguration conf) throws IOException, InterruptedException, KeeperException {
// Create the ZooKeeper client instance
ZooKeeperWatcherBase w = new ZooKeeperWatcherBase(conf.getZkTimeout());
zk = ZkUtils.createConnectedZookeeperClient(conf.getZkServers(), w);
// Create the bookie path
bookiesPath = conf.getZkAvailableBookiesPath();
// Create the BookKeeper client instance
bkc = new BookKeeper(conf, zk);
this.lfr = new LedgerFragmentReplicator(bkc);
}
/**
* Constructor that takes in a BookKeeper instance . This will be useful,
* when users already has bk instance ready.
*
* @param bkc
* - bookkeeper instance
*/
public BookKeeperAdmin(final BookKeeper bkc) {
this.bkc = bkc;
this.zk = bkc.zk;
this.bookiesPath = bkc.getConf().getZkAvailableBookiesPath();
this.lfr = new LedgerFragmentReplicator(bkc);
}
/**
* Gracefully release resources that this client uses.
*
* @throws InterruptedException
* if there is an error shutting down the clients that this
* class uses.
*/
public void close() throws InterruptedException, BKException {
bkc.close();
zk.close();
}
/**
* Get a list of the available bookies.
*
* @return a collection of bookie addresses
*/
public Collection getAvailableBookies()
throws BKException {
return bkc.bookieWatcher.getBookies();
}
/**
* Get a list of readonly bookies
*
* @return a collection of bookie addresses
*/
public Collection getReadOnlyBookies() {
return bkc.bookieWatcher.getReadOnlyBookies();
}
/**
* Notify when the available list of bookies changes.
* This is a one-shot notification. To receive subsequent notifications
* the listener must be registered again.
*
* @param listener the listener to notify
*/
public void notifyBookiesChanged(final BookiesListener listener)
throws BKException {
bkc.bookieWatcher.notifyBookiesChanged(listener);
}
/**
* Open a ledger as an administrator. This means that no digest password
* checks are done. Otherwise, the call is identical to BookKeeper#asyncOpenLedger
*
* @param lId
* ledger identifier
* @param cb
* Callback which will receive a LedgerHandle object
* @param ctx
* optional context object, to be passwd to the callback (can be null)
*
* @see BookKeeper#asyncOpenLedger
*/
public void asyncOpenLedger(final long lId, final OpenCallback cb, final Object ctx) {
new LedgerOpenOp(bkc, lId, cb, ctx).initiate();
}
/**
* Open a ledger as an administrator. This means that no digest password
* checks are done. Otherwise, the call is identical to
* BookKeeper#openLedger
*
* @param lId
* - ledger identifier
* @see BookKeeper#openLedger
*/
public LedgerHandle openLedger(final long lId) throws InterruptedException,
BKException {
SyncCounter counter = new SyncCounter();
counter.inc();
new LedgerOpenOp(bkc, lId, new SyncOpenCallback(), counter).initiate();
/*
* Wait
*/
counter.block(0);
if (counter.getrc() != BKException.Code.OK) {
throw BKException.create(counter.getrc());
}
return counter.getLh();
}
/**
* Open a ledger as an administrator without recovering the ledger. This means
* that no digest password checks are done. Otherwise, the call is identical
* to BookKeeper#asyncOpenLedgerNoRecovery
*
* @param lId
* ledger identifier
* @param cb
* Callback which will receive a LedgerHandle object
* @param ctx
* optional context object, to be passwd to the callback (can be null)
*
* @see BookKeeper#asyncOpenLedgerNoRecovery
*/
public void asyncOpenLedgerNoRecovery(final long lId, final OpenCallback cb, final Object ctx) {
new LedgerOpenOp(bkc, lId, cb, ctx).initiateWithoutRecovery();
}
/**
* Open a ledger as an administrator without recovering the ledger. This
* means that no digest password checks are done. Otherwise, the call is
* identical to BookKeeper#openLedgerNoRecovery
*
* @param lId
* ledger identifier
* @see BookKeeper#openLedgerNoRecovery
*/
public LedgerHandle openLedgerNoRecovery(final long lId)
throws InterruptedException, BKException {
SyncCounter counter = new SyncCounter();
counter.inc();
new LedgerOpenOp(bkc, lId, new SyncOpenCallback(), counter)
.initiateWithoutRecovery();
/*
* Wait
*/
counter.block(0);
if (counter.getrc() != BKException.Code.OK) {
throw BKException.create(counter.getrc());
}
return counter.getLh();
}
// Object used for calling async methods and waiting for them to complete.
static class SyncObject {
boolean value;
int rc;
public SyncObject() {
value = false;
rc = BKException.Code.OK;
}
}
/**
* Synchronous method to rebuild and recover the ledger fragments data that
* was stored on the source bookie. That bookie could have failed completely
* and now the ledger data that was stored on it is under replicated. An
* optional destination bookie server could be given if we want to copy all
* of the ledger fragments data on the failed source bookie to it.
* Otherwise, we will just randomly distribute the ledger fragments to the
* active set of bookies, perhaps based on load. All ZooKeeper ledger
* metadata will be updated to point to the new bookie(s) that contain the
* replicated ledger fragments.
*
* @param bookieSrc
* Source bookie that had a failure. We want to replicate the
* ledger fragments that were stored there.
* @param bookieDest
* Optional destination bookie that if passed, we will copy all
* of the ledger fragments from the source bookie over to it.
*/
public void recoverBookieData(final InetSocketAddress bookieSrc, final InetSocketAddress bookieDest)
throws InterruptedException, BKException {
SyncObject sync = new SyncObject();
// Call the async method to recover bookie data.
asyncRecoverBookieData(bookieSrc, bookieDest, new RecoverCallback() {
@Override
public void recoverComplete(int rc, Object ctx) {
LOG.info("Recover bookie operation completed with rc: " + rc);
SyncObject syncObj = (SyncObject) ctx;
synchronized (syncObj) {
syncObj.rc = rc;
syncObj.value = true;
syncObj.notify();
}
}
}, sync);
// Wait for the async method to complete.
synchronized (sync) {
while (sync.value == false) {
sync.wait();
}
}
if (sync.rc != BKException.Code.OK) {
throw BKException.create(sync.rc);
}
}
/**
* Async method to rebuild and recover the ledger fragments data that was
* stored on the source bookie. That bookie could have failed completely and
* now the ledger data that was stored on it is under replicated. An
* optional destination bookie server could be given if we want to copy all
* of the ledger fragments data on the failed source bookie to it.
* Otherwise, we will just randomly distribute the ledger fragments to the
* active set of bookies, perhaps based on load. All ZooKeeper ledger
* metadata will be updated to point to the new bookie(s) that contain the
* replicated ledger fragments.
*
* @param bookieSrc
* Source bookie that had a failure. We want to replicate the
* ledger fragments that were stored there.
* @param bookieDest
* Optional destination bookie that if passed, we will copy all
* of the ledger fragments from the source bookie over to it.
* @param cb
* RecoverCallback to invoke once all of the data on the dead
* bookie has been recovered and replicated.
* @param context
* Context for the RecoverCallback to call.
*/
public void asyncRecoverBookieData(final InetSocketAddress bookieSrc, final InetSocketAddress bookieDest,
final RecoverCallback cb, final Object context) {
// Sync ZK to make sure we're reading the latest bookie data.
zk.sync(bookiesPath, new AsyncCallback.VoidCallback() {
@Override
public void processResult(int rc, String path, Object ctx) {
if (rc != Code.OK.intValue()) {
LOG.error("ZK error syncing: ", KeeperException.create(KeeperException.Code.get(rc), path));
cb.recoverComplete(BKException.Code.ZKException, context);
return;
}
getAvailableBookies(bookieSrc, bookieDest, cb, context);
};
}, null);
}
/**
* This method asynchronously gets the set of available Bookies that the
* dead input bookie's data will be copied over into. If the user passed in
* a specific destination bookie, then just use that one. Otherwise, we'll
* randomly pick one of the other available bookies to use for each ledger
* fragment we are replicating.
*
* @param bookieSrc
* Source bookie that had a failure. We want to replicate the
* ledger fragments that were stored there.
* @param bookieDest
* Optional destination bookie that if passed, we will copy all
* of the ledger fragments from the source bookie over to it.
* @param cb
* RecoverCallback to invoke once all of the data on the dead
* bookie has been recovered and replicated.
* @param context
* Context for the RecoverCallback to call.
*/
private void getAvailableBookies(final InetSocketAddress bookieSrc, final InetSocketAddress bookieDest,
final RecoverCallback cb, final Object context) {
final List availableBookies = new LinkedList();
if (bookieDest != null) {
availableBookies.add(bookieDest);
// Now poll ZK to get the active ledgers
getActiveLedgers(bookieSrc, bookieDest, cb, context, availableBookies);
} else {
zk.getChildren(bookiesPath, null, new AsyncCallback.ChildrenCallback() {
@Override
public void processResult(int rc, String path, Object ctx, List children) {
if (rc != Code.OK.intValue()) {
LOG.error("ZK error getting bookie nodes: ", KeeperException.create(KeeperException.Code
.get(rc), path));
cb.recoverComplete(BKException.Code.ZKException, context);
return;
}
for (String bookieNode : children) {
if (BookKeeperConstants.READONLY
.equals(bookieNode)) {
// exclude the readonly node from available bookies.
continue;
}
String parts[] = bookieNode.split(BookKeeperConstants.COLON);
if (parts.length < 2) {
LOG.error("Bookie Node retrieved from ZK has invalid name format: " + bookieNode);
cb.recoverComplete(BKException.Code.ZKException, context);
return;
}
availableBookies.add(new InetSocketAddress(parts[0], Integer.parseInt(parts[1])));
}
// Now poll ZK to get the active ledgers
getActiveLedgers(bookieSrc, null, cb, context, availableBookies);
}
}, null);
}
}
/**
* This method asynchronously polls ZK to get the current set of active
* ledgers. From this, we can open each ledger and look at the metadata to
* determine if any of the ledger fragments for it were stored at the dead
* input bookie.
*
* @param bookieSrc
* Source bookie that had a failure. We want to replicate the
* ledger fragments that were stored there.
* @param bookieDest
* Optional destination bookie that if passed, we will copy all
* of the ledger fragments from the source bookie over to it.
* @param cb
* RecoverCallback to invoke once all of the data on the dead
* bookie has been recovered and replicated.
* @param context
* Context for the RecoverCallback to call.
* @param availableBookies
* List of Bookie Servers that are available to use for
* replicating data on the failed bookie. This could contain a
* single bookie server if the user explicitly chose a bookie
* server to replicate data to.
*/
private void getActiveLedgers(final InetSocketAddress bookieSrc, final InetSocketAddress bookieDest,
final RecoverCallback cb, final Object context, final List availableBookies) {
// Wrapper class around the RecoverCallback so it can be used
// as the final VoidCallback to process ledgers
class RecoverCallbackWrapper implements AsyncCallback.VoidCallback {
final RecoverCallback cb;
RecoverCallbackWrapper(RecoverCallback cb) {
this.cb = cb;
}
@Override
public void processResult(int rc, String path, Object ctx) {
cb.recoverComplete(rc, ctx);
}
}
Processor ledgerProcessor = new Processor() {
@Override
public void process(Long ledgerId, AsyncCallback.VoidCallback iterCallback) {
recoverLedger(bookieSrc, ledgerId, iterCallback, availableBookies);
}
};
bkc.getLedgerManager().asyncProcessLedgers(
ledgerProcessor, new RecoverCallbackWrapper(cb),
context, BKException.Code.OK, BKException.Code.LedgerRecoveryException);
}
/**
* Get a new random bookie, but ensure that it isn't one that is already
* in the ensemble for the ledger.
*/
private InetSocketAddress getNewBookie(final List bookiesAlreadyInEnsemble,
final List availableBookies)
throws BKException.BKNotEnoughBookiesException {
ArrayList candidates = new ArrayList();
candidates.addAll(availableBookies);
candidates.removeAll(bookiesAlreadyInEnsemble);
if (candidates.size() == 0) {
throw new BKException.BKNotEnoughBookiesException();
}
return candidates.get(rand.nextInt(candidates.size()));
}
/**
* This method asynchronously recovers a given ledger if any of the ledger
* entries were stored on the failed bookie.
*
* @param bookieSrc
* Source bookie that had a failure. We want to replicate the
* ledger fragments that were stored there.
* @param lId
* Ledger id we want to recover.
* @param ledgerIterCb
* IterationCallback to invoke once we've recovered the current
* ledger.
* @param availableBookies
* List of Bookie Servers that are available to use for
* replicating data on the failed bookie. This could contain a
* single bookie server if the user explicitly chose a bookie
* server to replicate data to.
*/
private void recoverLedger(final InetSocketAddress bookieSrc, final long lId,
final AsyncCallback.VoidCallback ledgerIterCb, final List availableBookies) {
LOG.debug("Recovering ledger : {}", lId);
asyncOpenLedgerNoRecovery(lId, new OpenCallback() {
@Override
public void openComplete(int rc, final LedgerHandle lh, Object ctx) {
if (rc != Code.OK.intValue()) {
LOG.error("BK error opening ledger: " + lId, BKException.create(rc));
ledgerIterCb.processResult(rc, null, null);
return;
}
LedgerMetadata lm = lh.getLedgerMetadata();
if (!lm.isClosed() &&
lm.getEnsembles().size() > 0) {
Long lastKey = lm.getEnsembles().lastKey();
ArrayList lastEnsemble = lm.getEnsembles().get(lastKey);
// the original write has not removed faulty bookie from
// current ledger ensemble. to avoid data loss issue in
// the case of concurrent updates to the ensemble composition,
// the recovery tool should first close the ledger
if (lastEnsemble.contains(bookieSrc)) {
// close opened non recovery ledger handle
try {
lh.close();
} catch (Exception ie) {
LOG.warn("Error closing non recovery ledger handle for ledger " + lId, ie);
}
asyncOpenLedger(lId, new OpenCallback() {
@Override
public void openComplete(int newrc, final LedgerHandle newlh, Object newctx) {
if (newrc != Code.OK.intValue()) {
LOG.error("BK error close ledger: " + lId, BKException.create(newrc));
ledgerIterCb.processResult(newrc, null, null);
return;
}
// do recovery
recoverLedger(bookieSrc, lId, ledgerIterCb, availableBookies);
}
}, null);
return;
}
}
/*
* This List stores the ledger fragments to recover indexed by
* the start entry ID for the range. The ensembles TreeMap is
* keyed off this.
*/
final List ledgerFragmentsToRecover = new LinkedList();
/*
* This Map will store the start and end entry ID values for
* each of the ledger fragment ranges. The only exception is the
* current active fragment since it has no end yet. In the event
* of a bookie failure, a new ensemble is created so the current
* ensemble should not contain the dead bookie we are trying to
* recover.
*/
Map ledgerFragmentsRange = new HashMap();
Long curEntryId = null;
for (Map.Entry> entry : lh.getLedgerMetadata().getEnsembles()
.entrySet()) {
if (curEntryId != null)
ledgerFragmentsRange.put(curEntryId, entry.getKey() - 1);
curEntryId = entry.getKey();
if (entry.getValue().contains(bookieSrc)) {
/*
* Current ledger fragment has entries stored on the
* dead bookie so we'll need to recover them.
*/
ledgerFragmentsToRecover.add(entry.getKey());
}
}
// add last ensemble otherwise if the failed bookie existed in
// the last ensemble of a closed ledger. the entries belonged to
// last ensemble would not be replicated.
if (curEntryId != null) {
ledgerFragmentsRange.put(curEntryId, lh.getLastAddConfirmed());
}
/*
* See if this current ledger contains any ledger fragment that
* needs to be re-replicated. If not, then just invoke the
* multiCallback and return.
*/
if (ledgerFragmentsToRecover.size() == 0) {
ledgerIterCb.processResult(BKException.Code.OK, null, null);
return;
}
/*
* Multicallback for ledger. Once all fragments for the ledger have been recovered
* trigger the ledgerIterCb
*/
MultiCallback ledgerFragmentsMcb
= new MultiCallback(ledgerFragmentsToRecover.size(), ledgerIterCb, null,
BKException.Code.OK, BKException.Code.LedgerRecoveryException);
/*
* Now recover all of the necessary ledger fragments
* asynchronously using a MultiCallback for every fragment.
*/
for (final Long startEntryId : ledgerFragmentsToRecover) {
Long endEntryId = ledgerFragmentsRange.get(startEntryId);
InetSocketAddress newBookie = null;
try {
newBookie = getNewBookie(lh.getLedgerMetadata().getEnsembles().get(startEntryId),
availableBookies);
} catch (BKException.BKNotEnoughBookiesException bke) {
ledgerFragmentsMcb.processResult(BKException.Code.NotEnoughBookiesException,
null, null);
continue;
}
if (LOG.isDebugEnabled()) {
LOG.debug("Replicating fragment from [" + startEntryId
+ "," + endEntryId + "] of ledger " + lh.getId()
+ " to " + newBookie);
}
try {
LedgerFragmentReplicator.SingleFragmentCallback cb = new LedgerFragmentReplicator.SingleFragmentCallback(
ledgerFragmentsMcb, lh, startEntryId, bookieSrc, newBookie);
ArrayList currentEnsemble = lh.getLedgerMetadata().getEnsemble(startEntryId);
int bookieIndex = -1;
if (null != currentEnsemble) {
for (int i = 0; i < currentEnsemble.size(); i++) {
if (currentEnsemble.get(i).equals(bookieSrc)) {
bookieIndex = i;
break;
}
}
}
LedgerFragment ledgerFragment = new LedgerFragment(lh,
startEntryId, endEntryId, bookieIndex);
asyncRecoverLedgerFragment(lh, ledgerFragment, cb, newBookie);
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
}
}, null);
}
/**
* This method asynchronously recovers a ledger fragment which is a
* contiguous portion of a ledger that was stored in an ensemble that
* included the failed bookie.
*
* @param lh
* - LedgerHandle for the ledger
* @param lf
* - LedgerFragment to replicate
* @param ledgerFragmentMcb
* - MultiCallback to invoke once we've recovered the current
* ledger fragment.
* @param newBookie
* - New bookie we want to use to recover and replicate the
* ledger entries that were stored on the failed bookie.
*/
private void asyncRecoverLedgerFragment(final LedgerHandle lh,
final LedgerFragment ledgerFragment,
final AsyncCallback.VoidCallback ledgerFragmentMcb,
final InetSocketAddress newBookie) throws InterruptedException {
lfr.replicate(lh, ledgerFragment, ledgerFragmentMcb, newBookie);
}
/**
* Replicate the Ledger fragment to target Bookie passed.
*
* @param lh
* - ledgerHandle
* @param ledgerFragment
* - LedgerFragment to replicate
* @param targetBookieAddress
* - target Bookie, to where entries should be replicated.
*/
public void replicateLedgerFragment(LedgerHandle lh,
final LedgerFragment ledgerFragment,
final InetSocketAddress targetBookieAddress)
throws InterruptedException, BKException {
SyncCounter syncCounter = new SyncCounter();
ResultCallBack resultCallBack = new ResultCallBack(syncCounter);
SingleFragmentCallback cb = new SingleFragmentCallback(resultCallBack,
lh, ledgerFragment.getFirstEntryId(), ledgerFragment
.getAddress(), targetBookieAddress);
syncCounter.inc();
asyncRecoverLedgerFragment(lh, ledgerFragment, cb, targetBookieAddress);
syncCounter.block(0);
if (syncCounter.getrc() != BKException.Code.OK) {
throw BKException.create(syncCounter.getrc());
}
}
/** This is the class for getting the replication result */
static class ResultCallBack implements AsyncCallback.VoidCallback {
private SyncCounter sync;
public ResultCallBack(SyncCounter sync) {
this.sync = sync;
}
@Override
public void processResult(int rc, String s, Object obj) {
sync.setrc(rc);
sync.dec();
}
}
/**
* Format the BookKeeper metadata in zookeeper
*
* @param isInteractive
* Whether format should ask prompt for confirmation if old data
* exists or not.
* @param force
* If non interactive and force is true, then old data will be
* removed without prompt.
* @return Returns true if format succeeds else false.
*/
public static boolean format(ClientConfiguration conf,
boolean isInteractive, boolean force) throws Exception {
ZooKeeperWatcherBase w = new ZooKeeperWatcherBase(conf.getZkTimeout());
ZooKeeper zkc = ZkUtils.createConnectedZookeeperClient(
conf.getZkServers(), w);
BookKeeper bkc = null;
try {
boolean ledgerRootExists = null != zkc.exists(
conf.getZkLedgersRootPath(), false);
boolean availableNodeExists = null != zkc.exists(
conf.getZkAvailableBookiesPath(), false);
// Create ledgers root node if not exists
if (!ledgerRootExists) {
zkc.create(conf.getZkLedgersRootPath(), "".getBytes(),
Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
// create available bookies node if not exists
if (!availableNodeExists) {
zkc.create(conf.getZkAvailableBookiesPath(), "".getBytes(),
Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
// If old data was there then confirm with admin.
if (ledgerRootExists) {
boolean confirm = false;
if (!isInteractive) {
// If non interactive and force is set, then delete old
// data.
if (force) {
confirm = true;
} else {
confirm = false;
}
} else {
// Confirm with the admin.
confirm = IOUtils
.confirmPrompt("Ledger root already exists. "
+"Are you sure to format bookkeeper metadata? "
+"This may cause data loss.");
}
if (!confirm) {
LOG.error("BookKeeper metadata Format aborted!!");
return false;
}
}
bkc = new BookKeeper(conf, zkc);
// Format all ledger metadata layout
bkc.ledgerManagerFactory.format(conf, zkc);
// Clear the cookies
try {
ZKUtil.deleteRecursive(zkc, conf.getZkLedgersRootPath()
+ "/cookies");
} catch (KeeperException.NoNodeException e) {
LOG.debug("cookies node not exists in zookeeper to delete");
}
// Clear the INSTANCEID
try {
zkc.delete(conf.getZkLedgersRootPath() + "/"
+ BookKeeperConstants.INSTANCEID, -1);
} catch (KeeperException.NoNodeException e) {
LOG.debug("INSTANCEID not exists in zookeeper to delete");
}
// create INSTANCEID
String instanceId = UUID.randomUUID().toString();
zkc.create(conf.getZkLedgersRootPath() + "/"
+ BookKeeperConstants.INSTANCEID, instanceId.getBytes(),
Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
LOG.info("Successfully formatted BookKeeper metadata");
} finally {
if (null != bkc) {
bkc.close();
}
if (null != zkc) {
zkc.close();
}
}
return true;
}
/**
* This method returns an iterable object for the list of ledger identifiers of
* the ledgers currently available.
*
* @return an iterable object for the list of ledger identifiers
* @throws IOException if the list of ledger identifiers cannot be read from the
* metadata store
*/
public Iterable listLedgers()
throws IOException {
final LedgerRangeIterator iterator = bkc.getLedgerManager().getLedgerRanges();
return new Iterable() {
public Iterator iterator() {
return new Iterator() {
Iterator currentRange = null;
@Override
public boolean hasNext() {
try {
if (iterator.hasNext()) {
LOG.info("I'm in this part of");
return true;
} else if (currentRange != null) {
if (currentRange.hasNext()) {
return true;
}
}
} catch (IOException e) {
LOG.error("Error while checking if there is a next element", e);
}
return false;
}
@Override
public Long next()
throws NoSuchElementException {
try{
if (currentRange == null) {
currentRange = iterator.next().getLedgers().iterator();
}
} catch (IOException e) {
LOG.error("Error while reading the next element", e);
throw new NoSuchElementException(e.getMessage());
}
return currentRange.next();
}
@Override
public void remove()
throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
};
}
};
}
/**
* @return the metadata for the passed ledger handle
*/
public LedgerMetadata getLedgerMetadata(LedgerHandle lh) {
return lh.getLedgerMetadata();
}
}
BookieWatcher.java 0000664 0000000 0000000 00000033104 12445073612 0034306 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/client 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.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.apache.bookkeeper.client.BKException.BKNotEnoughBookiesException;
import org.apache.bookkeeper.conf.ClientConfiguration;
import org.apache.bookkeeper.util.BookKeeperConstants;
import org.apache.bookkeeper.util.SafeRunnable;
import org.apache.bookkeeper.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.AsyncCallback.ChildrenCallback;
import org.apache.zookeeper.KeeperException.Code;
import org.apache.zookeeper.KeeperException.NodeExistsException;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.ZooDefs.Ids;
/**
* This class is responsible for maintaining a consistent view of what bookies
* are available by reading Zookeeper (and setting watches on the bookie nodes).
* When a bookie fails, the other parts of the code turn to this class to find a
* replacement
*
*/
class BookieWatcher implements Watcher, ChildrenCallback {
static final Logger logger = LoggerFactory.getLogger(BookieWatcher.class);
// Bookie registration path in ZK
private final String bookieRegistrationPath;
static final Set EMPTY_SET = new HashSet();
public static int ZK_CONNECT_BACKOFF_SEC = 1;
final BookKeeper bk;
HashSet knownBookies = new HashSet();
final ScheduledExecutorService scheduler;
SafeRunnable reReadTask = new SafeRunnable() {
@Override
public void safeRun() {
readBookies();
}
};
private ReadOnlyBookieWatcher readOnlyBookieWatcher;
public BookieWatcher(ClientConfiguration conf,
ScheduledExecutorService scheduler,
BookKeeper bk) throws KeeperException, InterruptedException {
this.bk = bk;
// ZK bookie registration path
this.bookieRegistrationPath = conf.getZkAvailableBookiesPath();
this.scheduler = scheduler;
readOnlyBookieWatcher = new ReadOnlyBookieWatcher(conf, bk);
}
void notifyBookiesChanged(final BookiesListener listener) throws BKException {
try {
bk.getZkHandle().getChildren(this.bookieRegistrationPath,
new Watcher() {
public void process(WatchedEvent event) {
// listen children changed event from ZooKeeper
if (event.getType() == EventType.NodeChildrenChanged) {
listener.availableBookiesChanged();
}
}
});
} catch (KeeperException ke) {
logger.error("Error registering watcher with zookeeper", ke);
throw new BKException.ZKException();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
logger.error("Interrupted registering watcher with zookeeper", ie);
throw new BKException.BKInterruptedException();
}
}
public Collection getBookies() throws BKException {
try {
List children = bk.getZkHandle().getChildren(this.bookieRegistrationPath, false);
children.remove(BookKeeperConstants.READONLY);
return convertToBookieAddresses(children);
} catch (KeeperException ke) {
logger.error("Failed to get bookie list : ", ke);
throw new BKException.ZKException();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
logger.error("Interrupted reading bookie list", ie);
throw new BKException.BKInterruptedException();
}
}
Collection getReadOnlyBookies() {
return new HashSet(readOnlyBookieWatcher.getReadOnlyBookies());
}
public void readBookies() {
readBookies(this);
}
public void readBookies(ChildrenCallback callback) {
bk.getZkHandle().getChildren(this.bookieRegistrationPath, this, callback, null);
}
@Override
public void process(WatchedEvent event) {
readBookies();
}
@Override
public void processResult(int rc, String path, Object ctx, List children) {
if (rc != KeeperException.Code.OK.intValue()) {
//logger.error("Error while reading bookies", KeeperException.create(Code.get(rc), path));
// try the read after a second again
scheduler.schedule(reReadTask, ZK_CONNECT_BACKOFF_SEC, TimeUnit.SECONDS);
return;
}
// Just exclude the 'readonly' znode to exclude r-o bookies from
// available nodes list.
children.remove(BookKeeperConstants.READONLY);
HashSet newBookieAddrs = convertToBookieAddresses(children);
final HashSet deadBookies;
synchronized (this) {
deadBookies = (HashSet)knownBookies.clone();
deadBookies.removeAll(newBookieAddrs);
// No need to close readonly bookie clients.
deadBookies.removeAll(readOnlyBookieWatcher.getReadOnlyBookies());
knownBookies = newBookieAddrs;
}
if (bk.getBookieClient() != null) {
bk.getBookieClient().closeClients(deadBookies);
}
}
private static HashSet convertToBookieAddresses(List children) {
// Read the bookie addresses into a set for efficient lookup
HashSet newBookieAddrs = new HashSet();
for (String bookieAddrString : children) {
InetSocketAddress bookieAddr;
try {
bookieAddr = StringUtils.parseAddr(bookieAddrString);
} catch (IOException e) {
logger.error("Could not parse bookie address: " + bookieAddrString + ", ignoring this bookie");
continue;
}
newBookieAddrs.add(bookieAddr);
}
return newBookieAddrs;
}
/**
* Blocks until bookies are read from zookeeper, used in the {@link BookKeeper} constructor.
* @throws InterruptedException
* @throws KeeperException
*/
public void readBookiesBlocking() throws InterruptedException, KeeperException {
// Read readonly bookies first
readOnlyBookieWatcher.readROBookiesBlocking();
final LinkedBlockingQueue queue = new LinkedBlockingQueue();
readBookies(new ChildrenCallback() {
public void processResult(int rc, String path, Object ctx, List children) {
try {
BookieWatcher.this.processResult(rc, path, ctx, children);
queue.put(rc);
} catch (InterruptedException e) {
logger.error("Interruped when trying to read bookies in a blocking fashion");
throw new RuntimeException(e);
}
}
});
int rc = queue.take();
if (rc != KeeperException.Code.OK.intValue()) {
throw KeeperException.create(Code.get(rc));
}
}
/**
* Wrapper over the {@link #getAdditionalBookies(Set, int)} method when there is no exclusion list (or exisiting bookies)
* @param numBookiesNeeded
* @return
* @throws BKNotEnoughBookiesException
*/
public ArrayList getNewBookies(int numBookiesNeeded) throws BKNotEnoughBookiesException {
return getAdditionalBookies(EMPTY_SET, numBookiesNeeded);
}
/**
* Wrapper over the {@link #getAdditionalBookies(Set, int)} method when you just need 1 extra bookie
* @param existingBookies
* @return
* @throws BKNotEnoughBookiesException
*/
public InetSocketAddress getAdditionalBookie(List existingBookies)
throws BKNotEnoughBookiesException {
return getAdditionalBookies(new HashSet(existingBookies), 1).get(0);
}
/**
* Returns additional bookies given an exclusion list and how many are needed
* @param existingBookies
* @param numAdditionalBookiesNeeded
* @return
* @throws BKNotEnoughBookiesException
*/
public ArrayList getAdditionalBookies(Set existingBookies,
int numAdditionalBookiesNeeded) throws BKNotEnoughBookiesException {
ArrayList newBookies = new ArrayList();
if (numAdditionalBookiesNeeded <= 0) {
return newBookies;
}
List allBookies;
synchronized (this) {
allBookies = new ArrayList(knownBookies);
}
Collections.shuffle(allBookies);
for (InetSocketAddress bookie : allBookies) {
if (existingBookies.contains(bookie)) {
continue;
}
newBookies.add(bookie);
numAdditionalBookiesNeeded--;
if (numAdditionalBookiesNeeded == 0) {
return newBookies;
}
}
throw new BKNotEnoughBookiesException();
}
/**
* Watcher implementation to watch the readonly bookies under
* <available>/readonly
*/
private static class ReadOnlyBookieWatcher implements Watcher, ChildrenCallback {
private final static Logger LOG = LoggerFactory.getLogger(ReadOnlyBookieWatcher.class);
private HashSet readOnlyBookies = new HashSet();
private BookKeeper bk;
private String readOnlyBookieRegPath;
public ReadOnlyBookieWatcher(ClientConfiguration conf, BookKeeper bk) throws KeeperException,
InterruptedException {
this.bk = bk;
readOnlyBookieRegPath = conf.getZkAvailableBookiesPath() + "/"
+ BookKeeperConstants.READONLY;
if (null == bk.getZkHandle().exists(readOnlyBookieRegPath, false)) {
try {
bk.getZkHandle().create(readOnlyBookieRegPath, new byte[0], Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
} catch (NodeExistsException e) {
// this node is just now created by someone.
}
}
}
@Override
public void process(WatchedEvent event) {
readROBookies();
}
// read the readonly bookies in blocking fashion. Used only for first
// time.
void readROBookiesBlocking() throws InterruptedException, KeeperException {
final LinkedBlockingQueue queue = new LinkedBlockingQueue();
readROBookies(new ChildrenCallback() {
public void processResult(int rc, String path, Object ctx, List children) {
try {
ReadOnlyBookieWatcher.this.processResult(rc, path, ctx, children);
queue.put(rc);
} catch (InterruptedException e) {
logger.error("Interruped when trying to read readonly bookies in a blocking fashion");
throw new RuntimeException(e);
}
}
});
int rc = queue.take();
if (rc != KeeperException.Code.OK.intValue()) {
throw KeeperException.create(Code.get(rc));
}
}
// Read children and register watcher for readonly bookies path
void readROBookies(ChildrenCallback callback) {
bk.getZkHandle().getChildren(this.readOnlyBookieRegPath, this, callback, null);
}
void readROBookies() {
readROBookies(this);
}
@Override
public void processResult(int rc, String path, Object ctx, List children) {
if (rc != Code.OK.intValue()) {
LOG.error("Not able to read readonly bookies : ", KeeperException.create(Code.get(rc)));
return;
}
HashSet newReadOnlyBookies = convertToBookieAddresses(children);
readOnlyBookies = newReadOnlyBookies;
}
// returns the readonly bookies
public HashSet getReadOnlyBookies() {
return readOnlyBookies;
}
}
}
BookiesListener.java 0000664 0000000 0000000 00000001724 12445073612 0034664 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/client 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.
*/
/**
* Listener for the the available bookies changes.
*/
public interface BookiesListener {
void availableBookiesChanged();
}
CRC32DigestManager.java 0000664 0000000 0000000 00000003122 12445073612 0034764 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/client 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.nio.ByteBuffer;
import java.util.zip.CRC32;
class CRC32DigestManager extends DigestManager {
private final ThreadLocal crc = new ThreadLocal() {
@Override
protected CRC32 initialValue() {
return new CRC32();
}
};
public CRC32DigestManager(long ledgerId) {
super(ledgerId);
}
@Override
int getMacCodeLength() {
return 8;
}
@Override
byte[] getValueAndReset() {
byte[] value = new byte[8];
ByteBuffer buf = ByteBuffer.wrap(value);
buf.putLong(crc.get().getValue());
crc.get().reset();
return value;
}
@Override
void update(byte[] data, int offset, int length) {
crc.get().update(data, offset, length);
}
}
DigestManager.java 0000664 0000000 0000000 00000015716 12445073612 0034303 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/client 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.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import org.apache.bookkeeper.client.BKException.BKDigestMatchException;
import org.apache.bookkeeper.client.BookKeeper.DigestType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBufferInputStream;
import org.jboss.netty.buffer.ChannelBuffers;
/**
* This class takes an entry, attaches a digest to it and packages it with relevant
* data so that it can be shipped to the bookie. On the return side, it also
* gets a packet, checks that the digest matches, and extracts the original entry
* for the packet. Currently 2 types of digests are supported: MAC (based on SHA-1) and CRC32
*/
abstract class DigestManager {
static final Logger logger = LoggerFactory.getLogger(DigestManager.class);
static final int METADATA_LENGTH = 32;
long ledgerId;
abstract int getMacCodeLength();
void update(byte[] data) {
update(data, 0, data.length);
}
abstract void update(byte[] data, int offset, int length);
abstract byte[] getValueAndReset();
final int macCodeLength;
public DigestManager(long ledgerId) {
this.ledgerId = ledgerId;
macCodeLength = getMacCodeLength();
}
static DigestManager instantiate(long ledgerId, byte[] passwd, DigestType digestType) throws GeneralSecurityException {
switch(digestType) {
case MAC:
return new MacDigestManager(ledgerId, passwd);
case CRC32:
return new CRC32DigestManager(ledgerId);
default:
throw new GeneralSecurityException("Unknown checksum type: " + digestType);
}
}
/**
* Computes the digest for an entry and put bytes together for sending.
*
* @param entryId
* @param lastAddConfirmed
* @param length
* @param data
* @return
*/
public ChannelBuffer computeDigestAndPackageForSending(long entryId, long lastAddConfirmed, long length, byte[] data, int doffset, int dlength) {
byte[] bufferArray = new byte[METADATA_LENGTH + macCodeLength];
ByteBuffer buffer = ByteBuffer.wrap(bufferArray);
buffer.putLong(ledgerId);
buffer.putLong(entryId);
buffer.putLong(lastAddConfirmed);
buffer.putLong(length);
buffer.flip();
update(buffer.array(), 0, METADATA_LENGTH);
update(data, doffset, dlength);
byte[] digest = getValueAndReset();
buffer.limit(buffer.capacity());
buffer.position(METADATA_LENGTH);
buffer.put(digest);
buffer.flip();
return ChannelBuffers.wrappedBuffer(ChannelBuffers.wrappedBuffer(buffer), ChannelBuffers.wrappedBuffer(data, doffset, dlength));
}
private void verifyDigest(ChannelBuffer dataReceived) throws BKDigestMatchException {
verifyDigest(LedgerHandle.INVALID_ENTRY_ID, dataReceived, true);
}
private void verifyDigest(long entryId, ChannelBuffer dataReceived) throws BKDigestMatchException {
verifyDigest(entryId, dataReceived, false);
}
private void verifyDigest(long entryId, ChannelBuffer dataReceived, boolean skipEntryIdCheck)
throws BKDigestMatchException {
ByteBuffer dataReceivedBuffer = dataReceived.toByteBuffer();
byte[] digest;
if ((METADATA_LENGTH + macCodeLength) > dataReceived.readableBytes()) {
logger.error("Data received is smaller than the minimum for this digest type. "
+ " Either the packet it corrupt, or the wrong digest is configured. "
+ " Digest type: {}, Packet Length: {}",
this.getClass().getName(), dataReceived.readableBytes());
throw new BKDigestMatchException();
}
update(dataReceivedBuffer.array(), dataReceivedBuffer.position(), METADATA_LENGTH);
int offset = METADATA_LENGTH + macCodeLength;
update(dataReceivedBuffer.array(), dataReceivedBuffer.position() + offset, dataReceived.readableBytes() - offset);
digest = getValueAndReset();
for (int i = 0; i < digest.length; i++) {
if (digest[i] != dataReceived.getByte(METADATA_LENGTH + i)) {
logger.error("Mac mismatch for ledger-id: " + ledgerId + ", entry-id: " + entryId);
throw new BKDigestMatchException();
}
}
long actualLedgerId = dataReceived.readLong();
long actualEntryId = dataReceived.readLong();
if (actualLedgerId != ledgerId) {
logger.error("Ledger-id mismatch in authenticated message, expected: " + ledgerId + " , actual: "
+ actualLedgerId);
throw new BKDigestMatchException();
}
if (!skipEntryIdCheck && actualEntryId != entryId) {
logger.error("Entry-id mismatch in authenticated message, expected: " + entryId + " , actual: "
+ actualEntryId);
throw new BKDigestMatchException();
}
}
/**
* Verify that the digest matches and returns the data in the entry.
*
* @param entryId
* @param dataReceived
* @return
* @throws BKDigestMatchException
*/
ChannelBufferInputStream verifyDigestAndReturnData(long entryId, ChannelBuffer dataReceived)
throws BKDigestMatchException {
verifyDigest(entryId, dataReceived);
dataReceived.readerIndex(METADATA_LENGTH + macCodeLength);
return new ChannelBufferInputStream(dataReceived);
}
static class RecoveryData {
long lastAddConfirmed;
long length;
public RecoveryData(long lastAddConfirmed, long length) {
this.lastAddConfirmed = lastAddConfirmed;
this.length = length;
}
}
RecoveryData verifyDigestAndReturnLastConfirmed(ChannelBuffer dataReceived) throws BKDigestMatchException {
verifyDigest(dataReceived);
dataReceived.readerIndex(8);
dataReceived.readLong(); // skip unused entryId
long lastAddConfirmed = dataReceived.readLong();
long length = dataReceived.readLong();
return new RecoveryData(lastAddConfirmed, length);
}
}
DistributionSchedule.java 0000664 0000000 0000000 00000006131 12445073612 0035714 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/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.
*/
package org.apache.bookkeeper.client;
import java.util.List;
/**
* This interface determins how entries are distributed among bookies.
*
* Every entry gets replicated to some number of replicas. The first replica for
* an entry is given a replicaIndex of 0, and so on. To distribute write load,
* not all entries go to all bookies. Given an entry-id and replica index, an
* {@link DistributionSchedule} determines which bookie that replica should go
* to.
*/
interface DistributionSchedule {
/**
* return the set of bookie indices to send the message to
*/
public List getWriteSet(long entryId);
/**
* An ack set represents the set of bookies from which
* a response must be received so that an entry can be
* considered to be replicated on a quorum.
*/
public interface AckSet {
/**
* Add a bookie response and check if quorum has been met
* @return true if quorum has been met, false otherwise
*/
public boolean addBookieAndCheck(int bookieIndexHeardFrom);
/**
* Invalidate a previous bookie response.
* Used for reissuing write requests.
*/
public void removeBookie(int bookie);
}
/**
* Returns an ackset object, responses should be checked against this
*/
public AckSet getAckSet();
/**
* Interface to keep track of which bookies in an ensemble, an action
* has been performed for.
*/
public interface QuorumCoverageSet {
/**
* Add a bookie to the set, and check if all quorum in the set
* have had the action performed for it.
* @param bookieIndexHeardFrom Bookie we've just heard from
* @return whether all quorums have been covered
*/
public boolean addBookieAndCheckCovered(int bookieIndexHeardFrom);
}
public QuorumCoverageSet getCoverageSet();
/**
* Whether entry presents on given bookie index
*
* @param entryId
* - entryId to check the presence on given bookie index
* @param bookieIndex
* - bookie index on which it need to check the possible presence
* of the entry
* @return true if it has entry otherwise false.
*/
public boolean hasEntry(long entryId, int bookieIndex);
}
LedgerChecker.java 0000664 0000000 0000000 00000026076 12445073612 0034261 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/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.
*
*/
package org.apache.bookkeeper.client;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.Map;
import org.apache.bookkeeper.proto.BookieClient;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.GenericCallback;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.ReadEntryCallback;
import org.jboss.netty.buffer.ChannelBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetSocketAddress;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicInteger;
/**
*Checks the complete ledger and finds the UnderReplicated fragments if any
*/
public class LedgerChecker {
private static Logger LOG = LoggerFactory.getLogger(LedgerChecker.class);
public final BookieClient bookieClient;
static class InvalidFragmentException extends Exception {
private static final long serialVersionUID = 1467201276417062353L;
}
/**
* This will collect all the entry read call backs and finally it will give
* call back to previous call back API which is waiting for it once it meets
* the expected call backs from down
*/
private static class ReadManyEntriesCallback implements ReadEntryCallback {
AtomicBoolean completed = new AtomicBoolean(false);
final AtomicLong numEntries;
final LedgerFragment fragment;
final GenericCallback cb;
ReadManyEntriesCallback(long numEntries, LedgerFragment fragment,
GenericCallback cb) {
this.numEntries = new AtomicLong(numEntries);
this.fragment = fragment;
this.cb = cb;
}
public void readEntryComplete(int rc, long ledgerId, long entryId,
ChannelBuffer buffer, Object ctx) {
if (rc == BKException.Code.OK) {
if (numEntries.decrementAndGet() == 0
&& !completed.getAndSet(true)) {
cb.operationComplete(rc, fragment);
}
} else if (!completed.getAndSet(true)) {
cb.operationComplete(rc, fragment);
}
}
}
public LedgerChecker(BookKeeper bkc) {
bookieClient = bkc.getBookieClient();
}
private void verifyLedgerFragment(LedgerFragment fragment,
GenericCallback cb) throws InvalidFragmentException {
long firstStored = fragment.getFirstStoredEntryId();
long lastStored = fragment.getLastStoredEntryId();
if (firstStored == LedgerHandle.INVALID_ENTRY_ID) {
if (lastStored != LedgerHandle.INVALID_ENTRY_ID) {
throw new InvalidFragmentException();
}
cb.operationComplete(BKException.Code.OK, fragment);
return;
}
if (firstStored == lastStored) {
ReadManyEntriesCallback manycb = new ReadManyEntriesCallback(1,
fragment, cb);
bookieClient.readEntry(fragment.getAddress(), fragment
.getLedgerId(), firstStored, manycb, null);
} else {
ReadManyEntriesCallback manycb = new ReadManyEntriesCallback(2,
fragment, cb);
bookieClient.readEntry(fragment.getAddress(), fragment
.getLedgerId(), firstStored, manycb, null);
bookieClient.readEntry(fragment.getAddress(), fragment
.getLedgerId(), lastStored, manycb, null);
}
}
/**
* Callback for checking whether an entry exists or not.
* It is used to differentiate the cases where it has been written
* but now cannot be read, and where it never has been written.
*/
private static class EntryExistsCallback implements ReadEntryCallback {
AtomicBoolean entryMayExist = new AtomicBoolean(false);
final AtomicInteger numReads;
final GenericCallback cb;
EntryExistsCallback(int numReads,
GenericCallback cb) {
this.numReads = new AtomicInteger(numReads);
this.cb = cb;
}
public void readEntryComplete(int rc, long ledgerId, long entryId,
ChannelBuffer buffer, Object ctx) {
if (rc != BKException.Code.NoSuchEntryException) {
entryMayExist.set(true);
}
if (numReads.decrementAndGet() == 0) {
cb.operationComplete(rc, entryMayExist.get());
}
}
}
/**
* This will collect all the fragment read call backs and finally it will
* give call back to above call back API which is waiting for it once it
* meets the expected call backs from down
*/
private static class FullLedgerCallback implements
GenericCallback {
final Set badFragments;
final AtomicLong numFragments;
final GenericCallback> cb;
FullLedgerCallback(long numFragments,
GenericCallback> cb) {
badFragments = new HashSet();
this.numFragments = new AtomicLong(numFragments);
this.cb = cb;
}
public void operationComplete(int rc, LedgerFragment result) {
if (rc != BKException.Code.OK) {
badFragments.add(result);
}
if (numFragments.decrementAndGet() == 0) {
cb.operationComplete(BKException.Code.OK, badFragments);
}
}
}
/**
* Check that all the fragments in the passed in ledger, and report those
* which are missing.
*/
public void checkLedger(LedgerHandle lh,
final GenericCallback> cb) {
// build a set of all fragment replicas
final Set fragments = new HashSet();
Long curEntryId = null;
ArrayList curEnsemble = null;
for (Map.Entry> e : lh
.getLedgerMetadata().getEnsembles().entrySet()) {
if (curEntryId != null) {
for (int i = 0; i < curEnsemble.size(); i++) {
fragments.add(new LedgerFragment(lh, curEntryId,
e.getKey() - 1, i));
}
}
curEntryId = e.getKey();
curEnsemble = e.getValue();
}
/* Checking the last segment of the ledger can be complicated in some cases.
* In the case that the ledger is closed, we can just check the fragments of
* the segment as normal, except in the case that no entry was ever written,
* to the ledger, in which case we check no fragments.
* In the case that the ledger is open, but enough entries have been written,
* for lastAddConfirmed to be set above the start entry of the segment, we
* can also check as normal.
* However, if lastAddConfirmed cannot be trusted, such as when it's lower than
* the first entry id, or not set at all, we cannot be sure if there has been
* data written to the segment. For this reason, we have to send a read request
* to the bookies which should have the first entry. If they respond with
* NoSuchEntry we can assume it was never written. If they respond with anything
* else, we must assume the entry has been written, so we run the check.
*/
if (curEntryId != null && !(lh.getLedgerMetadata().isClosed() && lh.getLastAddConfirmed() < curEntryId)) {
long lastEntry = lh.getLastAddConfirmed();
if (lastEntry < curEntryId) {
lastEntry = curEntryId;
}
final Set finalSegmentFragments = new HashSet();
for (int i = 0; i < curEnsemble.size(); i++) {
finalSegmentFragments.add(new LedgerFragment(lh, curEntryId,
lastEntry, i));
}
// Check for the case that no last confirmed entry has
// been set.
if (curEntryId == lastEntry) {
final long entryToRead = curEntryId;
EntryExistsCallback eecb
= new EntryExistsCallback(lh.getLedgerMetadata().getWriteQuorumSize(),
new GenericCallback() {
public void operationComplete(int rc, Boolean result) {
if (result) {
fragments.addAll(finalSegmentFragments);
}
checkFragments(fragments, cb);
}
});
for (int bi : lh.getDistributionSchedule().getWriteSet(entryToRead)) {
InetSocketAddress addr = curEnsemble.get(bi);
bookieClient.readEntry(addr, lh.getId(),
entryToRead, eecb, null);
}
return;
} else {
fragments.addAll(finalSegmentFragments);
}
}
checkFragments(fragments, cb);
}
private void checkFragments(Set fragments,
GenericCallback> cb) {
if (fragments.size() == 0) { // no fragments to verify
cb.operationComplete(BKException.Code.OK, fragments);
return;
}
// verify all the collected fragment replicas
FullLedgerCallback allFragmentsCb = new FullLedgerCallback(fragments
.size(), cb);
for (LedgerFragment r : fragments) {
LOG.debug("Checking fragment {}", r);
try {
verifyLedgerFragment(r, allFragmentsCb);
} catch (InvalidFragmentException ife) {
LOG.error("Invalid fragment found : {}", r);
allFragmentsCb.operationComplete(
BKException.Code.IncorrectParameterException, r);
}
}
}
}
LedgerCreateOp.java 0000664 0000000 0000000 00000010255 12445073612 0034407 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/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.
*
*/
package org.apache.bookkeeper.client;
import java.net.InetSocketAddress;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import org.apache.bookkeeper.client.AsyncCallback.CreateCallback;
import org.apache.bookkeeper.client.BKException.BKNotEnoughBookiesException;
import org.apache.bookkeeper.client.BookKeeper.DigestType;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.GenericCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Encapsulates asynchronous ledger create operation
*
*/
class LedgerCreateOp implements GenericCallback {
static final Logger LOG = LoggerFactory.getLogger(LedgerCreateOp.class);
CreateCallback cb;
LedgerMetadata metadata;
LedgerHandle lh;
Object ctx;
byte[] passwd;
BookKeeper bk;
DigestType digestType;
/**
* Constructor
*
* @param bk
* BookKeeper object
* @param ensembleSize
* ensemble size
* @param quorumSize
* quorum size
* @param digestType
* digest type, either MAC or CRC32
* @param passwd
* passowrd
* @param cb
* callback implementation
* @param ctx
* optional control object
*/
LedgerCreateOp(BookKeeper bk, int ensembleSize,
int writeQuorumSize, int ackQuorumSize,
DigestType digestType,
byte[] passwd, CreateCallback cb, Object ctx) {
this.bk = bk;
this.metadata = new LedgerMetadata(ensembleSize, writeQuorumSize, ackQuorumSize, digestType, passwd);
this.digestType = digestType;
this.passwd = passwd;
this.cb = cb;
this.ctx = ctx;
}
/**
* Initiates the operation
*/
public void initiate() {
// allocate ensemble first
/*
* Adding bookies to ledger handle
*/
ArrayList ensemble;
try {
ensemble = bk.bookieWatcher.getNewBookies(metadata.getEnsembleSize());
} catch (BKNotEnoughBookiesException e) {
LOG.error("Not enough bookies to create ledger");
cb.createComplete(e.getCode(), null, this.ctx);
return;
}
/*
* Add ensemble to the configuration
*/
metadata.addEnsemble(0L, ensemble);
// create a ledger with metadata
bk.getLedgerManager().createLedger(metadata, this);
}
/**
* Callback when created ledger.
*/
@Override
public void operationComplete(int rc, Long ledgerId) {
if (BKException.Code.OK != rc) {
cb.createComplete(rc, null, this.ctx);
return;
}
try {
lh = new LedgerHandle(bk, ledgerId, metadata, digestType, passwd);
} catch (GeneralSecurityException e) {
LOG.error("Security exception while creating ledger: " + ledgerId, e);
cb.createComplete(BKException.Code.DigestNotInitializedException, null, this.ctx);
return;
} catch (NumberFormatException e) {
LOG.error("Incorrectly entered parameter throttle: " + bk.getConf().getThrottleValue(), e);
cb.createComplete(BKException.Code.IncorrectParameterException, null, this.ctx);
return;
}
// return the ledger handle back
cb.createComplete(BKException.Code.OK, lh, this.ctx);
}
}
LedgerDeleteOp.java 0000664 0000000 0000000 00000004501 12445073612 0034403 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/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.
*
*/
package org.apache.bookkeeper.client;
import org.apache.bookkeeper.client.AsyncCallback.DeleteCallback;
import org.apache.bookkeeper.util.OrderedSafeExecutor.OrderedSafeGenericCallback;
import org.apache.bookkeeper.versioning.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Encapsulates asynchronous ledger delete operation
*
*/
class LedgerDeleteOp extends OrderedSafeGenericCallback {
static final Logger LOG = LoggerFactory.getLogger(LedgerDeleteOp.class);
BookKeeper bk;
long ledgerId;
DeleteCallback cb;
Object ctx;
/**
* Constructor
*
* @param bk
* BookKeeper object
* @param ledgerId
* ledger Id
* @param cb
* callback implementation
* @param ctx
* optional control object
*/
LedgerDeleteOp(BookKeeper bk, long ledgerId, DeleteCallback cb, Object ctx) {
super(bk.mainWorkerPool, ledgerId);
this.bk = bk;
this.ledgerId = ledgerId;
this.cb = cb;
this.ctx = ctx;
}
/**
* Initiates the operation
*/
public void initiate() {
// Asynchronously delete the ledger from meta manager
// When this completes, it will invoke the callback method below.
bk.getLedgerManager().removeLedgerMetadata(ledgerId, Version.ANY, this);
}
/**
* Implements Delete Callback.
*/
@Override
public void safeOperationComplete(int rc, Void result) {
cb.deleteComplete(rc, this.ctx);
}
}
LedgerEntry.java 0000664 0000000 0000000 00000004663 12445073612 0034014 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/client 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.io.IOException;
import java.io.InputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.jboss.netty.buffer.ChannelBufferInputStream;
/**
* Ledger entry. Its a simple tuple containing the ledger id, the entry-id, and
* the entry content.
*
*/
public class LedgerEntry {
Logger LOG = LoggerFactory.getLogger(LedgerEntry.class);
long ledgerId;
long entryId;
long length;
ChannelBufferInputStream entryDataStream;
LedgerEntry(long lId, long eId) {
this.ledgerId = lId;
this.entryId = eId;
}
public long getLedgerId() {
return ledgerId;
}
public long getEntryId() {
return entryId;
}
public long getLength() {
return length;
}
public byte[] getEntry() {
try {
// In general, you can't rely on the available() method of an input
// stream, but ChannelBufferInputStream is backed by a byte[] so it
// accurately knows the # bytes available
byte[] ret = new byte[entryDataStream.available()];
entryDataStream.readFully(ret);
return ret;
} catch (IOException e) {
// The channelbufferinput stream doesnt really throw the
// ioexceptions, it just has to be in the signature because
// InputStream says so. Hence this code, should never be reached.
LOG.error("Unexpected IOException while reading from channel buffer", e);
return new byte[0];
}
}
public InputStream getEntryInputStream() {
return entryDataStream;
}
}
LedgerFragment.java 0000664 0000000 0000000 00000011115 12445073612 0034444 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/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.
*
*/
package org.apache.bookkeeper.client;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
/**
* Represents the entries of a segment of a ledger which are stored on a single
* bookie in the segments bookie ensemble.
*
* Used for checking and recovery
*/
public class LedgerFragment {
private final int bookieIndex;
private final List ensemble;
private final long firstEntryId;
private final long lastKnownEntryId;
private final long ledgerId;
private final DistributionSchedule schedule;
private final boolean isLedgerClosed;
LedgerFragment(LedgerHandle lh, long firstEntryId,
long lastKnownEntryId, int bookieIndex) {
this.ledgerId = lh.getId();
this.firstEntryId = firstEntryId;
this.lastKnownEntryId = lastKnownEntryId;
this.bookieIndex = bookieIndex;
this.ensemble = lh.getLedgerMetadata().getEnsemble(firstEntryId);
this.schedule = lh.getDistributionSchedule();
SortedMap> ensembles = lh
.getLedgerMetadata().getEnsembles();
this.isLedgerClosed = lh.getLedgerMetadata().isClosed()
|| !ensemble.equals(ensembles.get(ensembles.lastKey()));
}
/**
* Returns true, if and only if the ledger fragment will never be modified
* by any of the clients in future, otherwise false. i.e,
*
* - If ledger is in closed state, then no other clients can modify this
* fragment.
* - If ledger is not in closed state and the current fragment is not a
* last fragment, then no one will modify this fragment.
*
*/
public boolean isClosed() {
return isLedgerClosed;
}
long getLedgerId() {
return ledgerId;
}
long getFirstEntryId() {
return firstEntryId;
}
long getLastKnownEntryId() {
return lastKnownEntryId;
}
/**
* Gets the failedBookie address
*/
public InetSocketAddress getAddress() {
return ensemble.get(bookieIndex);
}
/**
* Gets the failedBookie index
*/
public int getBookiesIndex() {
return bookieIndex;
}
/**
* Gets the first stored entry id of the fragment in failed bookie.
*
* @return entryId
*/
public long getFirstStoredEntryId() {
long firstEntry = firstEntryId;
for (int i = 0; i < ensemble.size() && firstEntry <= lastKnownEntryId; i++) {
if (schedule.hasEntry(firstEntry, bookieIndex)) {
return firstEntry;
} else {
firstEntry++;
}
}
return LedgerHandle.INVALID_ENTRY_ID;
}
/**
* Gets the last stored entry id of the fragment in failed bookie.
*
* @return entryId
*/
public long getLastStoredEntryId() {
long lastEntry = lastKnownEntryId;
for (int i = 0; i < ensemble.size() && lastEntry >= firstEntryId; i++) {
if (schedule.hasEntry(lastEntry, bookieIndex)) {
return lastEntry;
} else {
lastEntry--;
}
}
return LedgerHandle.INVALID_ENTRY_ID;
}
/**
* Gets the ensemble of fragment
*
* @return the ensemble for the segment which this fragment is a part of
*/
public List getEnsemble() {
return this.ensemble;
}
@Override
public String toString() {
return String.format("Fragment(LedgerID: %d, FirstEntryID: %d[%d], "
+ "LastKnownEntryID: %d[%d], Host: %s, Closed: %s)", ledgerId, firstEntryId,
getFirstStoredEntryId(), lastKnownEntryId, getLastStoredEntryId(),
getAddress(), isLedgerClosed);
}
} LedgerFragmentReplicator.java 0000664 0000000 0000000 00000045576 12445073612 0036513 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/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.
*
*/
package org.apache.bookkeeper.client;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.bookkeeper.client.AsyncCallback.ReadCallback;
import org.apache.bookkeeper.proto.BookieProtocol;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.GenericCallback;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.MultiCallback;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.WriteCallback;
import org.apache.bookkeeper.util.OrderedSafeExecutor.OrderedSafeGenericCallback;
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.KeeperException.Code;
import org.jboss.netty.buffer.ChannelBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is the helper class for replicating the fragments from one bookie to
* another
*/
public class LedgerFragmentReplicator {
// BookKeeper instance
private BookKeeper bkc;
public LedgerFragmentReplicator(BookKeeper bkc) {
this.bkc = bkc;
}
private static Logger LOG = LoggerFactory
.getLogger(LedgerFragmentReplicator.class);
private void replicateFragmentInternal(final LedgerHandle lh,
final LedgerFragment lf,
final AsyncCallback.VoidCallback ledgerFragmentMcb,
final InetSocketAddress newBookie) throws InterruptedException {
if (!lf.isClosed()) {
LOG.error("Trying to replicate an unclosed fragment;"
+ " This is not safe {}", lf);
ledgerFragmentMcb.processResult(BKException.Code.UnclosedFragmentException,
null, null);
return;
}
Long startEntryId = lf.getFirstStoredEntryId();
Long endEntryId = lf.getLastStoredEntryId();
if (endEntryId == null) {
/*
* Ideally this should never happen if bookie failure is taken care
* of properly. Nothing we can do though in this case.
*/
LOG.warn("Dead bookie (" + lf.getAddress()
+ ") is still part of the current"
+ " active ensemble for ledgerId: " + lh.getId());
ledgerFragmentMcb.processResult(BKException.Code.OK, null, null);
return;
}
if (startEntryId > endEntryId) {
// for open ledger which there is no entry, the start entry id is 0,
// the end entry id is -1.
// we can return immediately to trigger forward read
ledgerFragmentMcb.processResult(BKException.Code.OK, null, null);
return;
}
/*
* Add all the entries to entriesToReplicate list from
* firstStoredEntryId to lastStoredEntryID.
*/
List entriesToReplicate = new LinkedList();
long lastStoredEntryId = lf.getLastStoredEntryId();
for (long i = lf.getFirstStoredEntryId(); i <= lastStoredEntryId; i++) {
entriesToReplicate.add(i);
}
/*
* Now asynchronously replicate all of the entries for the ledger
* fragment that were on the dead bookie.
*/
MultiCallback ledgerFragmentEntryMcb = new MultiCallback(
entriesToReplicate.size(), ledgerFragmentMcb, null, BKException.Code.OK,
BKException.Code.LedgerRecoveryException);
for (final Long entryId : entriesToReplicate) {
recoverLedgerFragmentEntry(entryId, lh, ledgerFragmentEntryMcb,
newBookie);
}
}
/**
* This method replicate a ledger fragment which is a contiguous portion of
* a ledger that was stored in an ensemble that included the failed bookie.
* It will Splits the fragment into multiple sub fragments by keeping the
* max entries up to the configured value of rereplicationEntryBatchSize and
* then it re-replicates that batched entry fragments one by one. After
* re-replication of all batched entry fragments, it will update the
* ensemble info with new Bookie once
*
* @param lh
* LedgerHandle for the ledger
* @param lf
* LedgerFragment to replicate
* @param ledgerFragmentMcb
* MultiCallback to invoke once we've recovered the current
* ledger fragment.
* @param targetBookieAddress
* New bookie we want to use to recover and replicate the ledger
* entries that were stored on the failed bookie.
*/
void replicate(final LedgerHandle lh, final LedgerFragment lf,
final AsyncCallback.VoidCallback ledgerFragmentMcb,
final InetSocketAddress targetBookieAddress)
throws InterruptedException {
Set partionedFragments = splitIntoSubFragments(lh, lf,
bkc.getConf().getRereplicationEntryBatchSize());
LOG.info("Fragment :" + lf + " is split into sub fragments :"
+ partionedFragments);
replicateNextBatch(lh, partionedFragments.iterator(),
ledgerFragmentMcb, targetBookieAddress);
}
/** Replicate the batched entry fragments one after other */
private void replicateNextBatch(final LedgerHandle lh,
final Iterator fragments,
final AsyncCallback.VoidCallback ledgerFragmentMcb,
final InetSocketAddress targetBookieAddress) {
if (fragments.hasNext()) {
try {
replicateFragmentInternal(lh, fragments.next(),
new AsyncCallback.VoidCallback() {
@Override
public void processResult(int rc, String v, Object ctx) {
if (rc != BKException.Code.OK) {
ledgerFragmentMcb.processResult(rc, null,
null);
} else {
replicateNextBatch(lh, fragments,
ledgerFragmentMcb,
targetBookieAddress);
}
}
}, targetBookieAddress);
} catch (InterruptedException e) {
ledgerFragmentMcb.processResult(
BKException.Code.InterruptedException, null, null);
Thread.currentThread().interrupt();
}
} else {
ledgerFragmentMcb.processResult(BKException.Code.OK, null, null);
}
}
/**
* Split the full fragment into batched entry fragments by keeping
* rereplicationEntryBatchSize of entries in each one and can treat them as
* sub fragments
*/
static Set splitIntoSubFragments(LedgerHandle lh,
LedgerFragment ledgerFragment, long rereplicationEntryBatchSize) {
Set fragments = new HashSet();
if (rereplicationEntryBatchSize <= 0) {
// rereplicationEntryBatchSize can not be 0 or less than 0,
// returning with the current fragment
fragments.add(ledgerFragment);
return fragments;
}
long firstEntryId = ledgerFragment.getFirstStoredEntryId();
long lastEntryId = ledgerFragment.getLastStoredEntryId();
long numberOfEntriesToReplicate = (lastEntryId - firstEntryId) + 1;
long splitsWithFullEntries = numberOfEntriesToReplicate
/ rereplicationEntryBatchSize;
if (splitsWithFullEntries == 0) {// only one fragment
fragments.add(ledgerFragment);
return fragments;
}
long fragmentSplitLastEntry = 0;
for (int i = 0; i < splitsWithFullEntries; i++) {
fragmentSplitLastEntry = (firstEntryId + rereplicationEntryBatchSize) - 1;
fragments.add(new LedgerFragment(lh, firstEntryId,
fragmentSplitLastEntry, ledgerFragment.getBookiesIndex()));
firstEntryId = fragmentSplitLastEntry + 1;
}
long lastSplitWithPartialEntries = numberOfEntriesToReplicate
% rereplicationEntryBatchSize;
if (lastSplitWithPartialEntries > 0) {
fragments.add(new LedgerFragment(lh, firstEntryId, firstEntryId
+ lastSplitWithPartialEntries - 1, ledgerFragment
.getBookiesIndex()));
}
return fragments;
}
/**
* This method asynchronously recovers a specific ledger entry by reading
* the values via the BookKeeper Client (which would read it from the other
* replicas) and then writing it to the chosen new bookie.
*
* @param entryId
* Ledger Entry ID to recover.
* @param lh
* LedgerHandle for the ledger
* @param ledgerFragmentEntryMcb
* MultiCallback to invoke once we've recovered the current
* ledger entry.
* @param newBookie
* New bookie we want to use to recover and replicate the ledger
* entries that were stored on the failed bookie.
*/
private void recoverLedgerFragmentEntry(final Long entryId,
final LedgerHandle lh,
final AsyncCallback.VoidCallback ledgerFragmentEntryMcb,
final InetSocketAddress newBookie) throws InterruptedException {
/*
* Read the ledger entry using the LedgerHandle. This will allow us to
* read the entry from one of the other replicated bookies other than
* the dead one.
*/
lh.asyncReadEntries(entryId, entryId, new ReadCallback() {
@Override
public void readComplete(int rc, LedgerHandle lh,
Enumeration seq, Object ctx) {
if (rc != Code.OK.intValue()) {
LOG.error("BK error reading ledger entry: " + entryId,
BKException.create(rc));
ledgerFragmentEntryMcb.processResult(rc, null, null);
return;
}
/*
* Now that we've read the ledger entry, write it to the new
* bookie we've selected.
*/
LedgerEntry entry = seq.nextElement();
byte[] data = entry.getEntry();
ChannelBuffer toSend = lh.getDigestManager()
.computeDigestAndPackageForSending(entryId,
lh.getLastAddConfirmed(), entry.getLength(),
data, 0, data.length);
bkc.getBookieClient().addEntry(newBookie, lh.getId(),
lh.getLedgerKey(), entryId, toSend,
new WriteCallback() {
@Override
public void writeComplete(int rc, long ledgerId,
long entryId, InetSocketAddress addr,
Object ctx) {
if (rc != Code.OK.intValue()) {
LOG.error(
"BK error writing entry for ledgerId: "
+ ledgerId + ", entryId: "
+ entryId + ", bookie: "
+ addr, BKException
.create(rc));
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Success writing ledger id "
+ ledgerId + ", entry id "
+ entryId + " to a new bookie "
+ addr + "!");
}
}
/*
* Pass the return code result up the chain with
* the parent callback.
*/
ledgerFragmentEntryMcb.processResult(rc, null,
null);
}
}, null, BookieProtocol.FLAG_RECOVERY_ADD);
}
}, null);
}
/**
* Callback for recovery of a single ledger fragment. Once the fragment has
* had all entries replicated, update the ensemble in zookeeper. Once
* finished propogate callback up to ledgerFragmentsMcb which should be a
* multicallback responsible for all fragments in a single ledger
*/
static class SingleFragmentCallback implements AsyncCallback.VoidCallback {
final AsyncCallback.VoidCallback ledgerFragmentsMcb;
final LedgerHandle lh;
final long fragmentStartId;
final InetSocketAddress oldBookie;
final InetSocketAddress newBookie;
SingleFragmentCallback(AsyncCallback.VoidCallback ledgerFragmentsMcb,
LedgerHandle lh, long fragmentStartId,
InetSocketAddress oldBookie, InetSocketAddress newBookie) {
this.ledgerFragmentsMcb = ledgerFragmentsMcb;
this.lh = lh;
this.fragmentStartId = fragmentStartId;
this.newBookie = newBookie;
this.oldBookie = oldBookie;
}
@Override
public void processResult(int rc, String path, Object ctx) {
if (rc != Code.OK.intValue()) {
LOG.error("BK error replicating ledger fragments for ledger: "
+ lh.getId(), BKException.create(rc));
ledgerFragmentsMcb.processResult(rc, null, null);
return;
}
updateEnsembleInfo(ledgerFragmentsMcb, fragmentStartId, lh,
oldBookie, newBookie);
}
}
/** Updates the ensemble with newBookie and notify the ensembleUpdatedCb */
private static void updateEnsembleInfo(
AsyncCallback.VoidCallback ensembleUpdatedCb, long fragmentStartId,
LedgerHandle lh, InetSocketAddress oldBookie,
InetSocketAddress newBookie) {
/*
* Update the ledger metadata's ensemble info to point to the new
* bookie.
*/
ArrayList ensemble = lh.getLedgerMetadata()
.getEnsembles().get(fragmentStartId);
int deadBookieIndex = ensemble.indexOf(oldBookie);
ensemble.remove(deadBookieIndex);
ensemble.add(deadBookieIndex, newBookie);
lh.writeLedgerConfig(new UpdateEnsembleCb(ensembleUpdatedCb,
fragmentStartId, lh, oldBookie, newBookie));
}
/**
* Update the ensemble data with newBookie. re-reads the metadata on
* MetadataVersionException and update ensemble again. On successfull
* updation, it will also notify to super call back
*/
private static class UpdateEnsembleCb implements GenericCallback {
final AsyncCallback.VoidCallback ensembleUpdatedCb;
final LedgerHandle lh;
final long fragmentStartId;
final InetSocketAddress oldBookie;
final InetSocketAddress newBookie;
public UpdateEnsembleCb(AsyncCallback.VoidCallback ledgerFragmentsMcb,
long fragmentStartId, LedgerHandle lh,
InetSocketAddress oldBookie, InetSocketAddress newBookie) {
this.ensembleUpdatedCb = ledgerFragmentsMcb;
this.lh = lh;
this.fragmentStartId = fragmentStartId;
this.newBookie = newBookie;
this.oldBookie = oldBookie;
}
@Override
public void operationComplete(int rc, Void result) {
if (rc == BKException.Code.MetadataVersionException) {
LOG.warn("Two fragments attempted update at once; ledger id: "
+ lh.getId() + " startid: " + fragmentStartId);
// try again, the previous success (with which this has
// conflicted) will have updated the stat other operations
// such as (addEnsemble) would update it too.
lh
.rereadMetadata(new OrderedSafeGenericCallback(
lh.bk.mainWorkerPool, lh.getId()) {
@Override
public void safeOperationComplete(int rc,
LedgerMetadata newMeta) {
if (rc != BKException.Code.OK) {
LOG
.error("Error reading updated ledger metadata for ledger "
+ lh.getId());
ensembleUpdatedCb.processResult(rc, null,
null);
} else {
lh.metadata = newMeta;
updateEnsembleInfo(ensembleUpdatedCb,
fragmentStartId, lh, oldBookie,
newBookie);
}
}
});
return;
} else if (rc != BKException.Code.OK) {
LOG.error("Error updating ledger config metadata for ledgerId "
+ lh.getId() + " : " + BKException.getMessage(rc));
} else {
LOG.info("Updated ZK for ledgerId: (" + lh.getId() + " : "
+ fragmentStartId
+ ") to point ledger fragments from old dead bookie: ("
+ oldBookie + ") to new bookie: (" + newBookie + ")");
}
/*
* Pass the return code result up the chain with the parent
* callback.
*/
ensembleUpdatedCb.processResult(rc, null, null);
}
}
}
LedgerHandle.java 0000664 0000000 0000000 00000125500 12445073612 0034100 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/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.
*
*/
package org.apache.bookkeeper.client;
import java.net.InetSocketAddress;
import java.security.GeneralSecurityException;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.RejectedExecutionException;
import org.apache.bookkeeper.client.AsyncCallback.ReadLastConfirmedCallback;
import org.apache.bookkeeper.client.BKException;
import org.apache.bookkeeper.client.AsyncCallback.AddCallback;
import org.apache.bookkeeper.client.AsyncCallback.CloseCallback;
import org.apache.bookkeeper.client.AsyncCallback.ReadCallback;
import org.apache.bookkeeper.client.BKException.BKNotEnoughBookiesException;
import org.apache.bookkeeper.client.BookKeeper.DigestType;
import org.apache.bookkeeper.client.LedgerMetadata;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.GenericCallback;
import org.apache.bookkeeper.util.OrderedSafeExecutor.OrderedSafeGenericCallback;
import org.apache.bookkeeper.proto.BookieProtocol;
import org.apache.bookkeeper.proto.DataFormats.LedgerMetadataFormat.State;
import org.apache.bookkeeper.util.SafeRunnable;
import com.google.common.util.concurrent.RateLimiter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.jboss.netty.buffer.ChannelBuffer;
/**
* Ledger handle contains ledger metadata and is used to access the read and
* write operations to a ledger.
*/
public class LedgerHandle {
final static Logger LOG = LoggerFactory.getLogger(LedgerHandle.class);
final byte[] ledgerKey;
LedgerMetadata metadata;
final BookKeeper bk;
final long ledgerId;
long lastAddPushed;
long lastAddConfirmed;
long length;
final DigestManager macManager;
final DistributionSchedule distributionSchedule;
final RateLimiter throttler;
/**
* Invalid entry id. This value is returned from methods which
* should return an entry id but there is no valid entry available.
*/
final static public long INVALID_ENTRY_ID = BookieProtocol.INVALID_ENTRY_ID;
final AtomicInteger blockAddCompletions = new AtomicInteger(0);
final Queue pendingAddOps = new ConcurrentLinkedQueue();
LedgerHandle(BookKeeper bk, long ledgerId, LedgerMetadata metadata,
DigestType digestType, byte[] password)
throws GeneralSecurityException, NumberFormatException {
this.bk = bk;
this.metadata = metadata;
if (metadata.isClosed()) {
lastAddConfirmed = lastAddPushed = metadata.getLastEntryId();
length = metadata.getLength();
} else {
lastAddConfirmed = lastAddPushed = INVALID_ENTRY_ID;
length = 0;
}
this.ledgerId = ledgerId;
this.throttler = RateLimiter.create(bk.getConf().getThrottleValue());
macManager = DigestManager.instantiate(ledgerId, password, digestType);
this.ledgerKey = MacDigestManager.genDigest("ledger", password);
distributionSchedule = new RoundRobinDistributionSchedule(
metadata.getWriteQuorumSize(), metadata.getAckQuorumSize(), metadata.getEnsembleSize());
}
/**
* Get the id of the current ledger
*
* @return the id of the ledger
*/
public long getId() {
return ledgerId;
}
/**
* Get the last confirmed entry id on this ledger. It reads
* the local state of the ledger handle, which is different
* from the readLastConfirmed call. In the case the ledger
* is not closed and the client is a reader, it is necessary
* to call readLastConfirmed to obtain an estimate of the
* last add operation that has been confirmed.
*
* @see #readLastConfirmed()
*
* @return the last confirmed entry id or {@link #INVALID_ENTRY_ID INVALID_ENTRY_ID} if no entry has been confirmed
*/
public long getLastAddConfirmed() {
return lastAddConfirmed;
}
/**
* Get the entry id of the last entry that has been enqueued for addition (but
* may not have possibly been persited to the ledger)
*
* @return the id of the last entry pushed or {@link #INVALID_ENTRY_ID INVALID_ENTRY_ID} if no entry has been pushed
*/
synchronized public long getLastAddPushed() {
return lastAddPushed;
}
/**
* Get the Ledger's key/password.
*
* @return byte array for the ledger's key/password.
*/
public byte[] getLedgerKey() {
return Arrays.copyOf(ledgerKey, ledgerKey.length);
}
/**
* Get the LedgerMetadata
*
* @return LedgerMetadata for the LedgerHandle
*/
LedgerMetadata getLedgerMetadata() {
return metadata;
}
/**
* Get the DigestManager
*
* @return DigestManager for the LedgerHandle
*/
DigestManager getDigestManager() {
return macManager;
}
/**
* Add to the length of the ledger in bytes.
*
* @param delta
* @return
*/
long addToLength(long delta) {
this.length += delta;
return this.length;
}
/**
* Returns the length of the ledger in bytes.
*
* @return the length of the ledger in bytes
*/
synchronized public long getLength() {
return this.length;
}
/**
* Get the Distribution Schedule
*
* @return DistributionSchedule for the LedgerHandle
*/
DistributionSchedule getDistributionSchedule() {
return distributionSchedule;
}
void writeLedgerConfig(GenericCallback writeCb) {
LOG.debug("Writing metadata to ledger manager: {}, {}", this.ledgerId, metadata.getVersion());
bk.getLedgerManager().writeLedgerMetadata(ledgerId, metadata, writeCb);
}
/**
* Close this ledger synchronously.
* @see #asyncClose
*/
public void close()
throws InterruptedException, BKException {
SyncCounter counter = new SyncCounter();
counter.inc();
asyncClose(new SyncCloseCallback(), counter);
counter.block(0);
if (counter.getrc() != BKException.Code.OK) {
throw BKException.create(counter.getrc());
}
}
/**
* Asynchronous close, any adds in flight will return errors.
*
* Closing a ledger will ensure that all clients agree on what the last entry
* of the ledger is. This ensures that, once the ledger has been closed, all
* reads from the ledger will return the same set of entries.
*
* @param cb
* callback implementation
* @param ctx
* control object
* @throws InterruptedException
*/
public void asyncClose(CloseCallback cb, Object ctx) {
asyncCloseInternal(cb, ctx, BKException.Code.LedgerClosedException);
}
/**
* Has the ledger been closed?
*/
public synchronized boolean isClosed() {
return metadata.isClosed();
}
/**
* Same as public version of asyncClose except that this one takes an
* additional parameter which is the return code to hand to all the pending
* add ops
*
* @param cb
* @param ctx
* @param rc
*/
void asyncCloseInternal(final CloseCallback cb, final Object ctx, final int rc) {
bk.mainWorkerPool.submitOrdered(ledgerId, new SafeRunnable() {
@Override
public void safeRun() {
final long prevLastEntryId;
final long prevLength;
final State prevState;
List pendingAdds;
if (isClosed()) {
// TODO: make ledger metadata immutable
// Although the metadata is already closed, we don't need to proceed zookeeper metadata update, but
// we still need to error out the pending add ops.
//
// There is a race condition a pending add op is enqueued, after a close op reset ledger metadata state
// to unclosed to resolve metadata conflicts. If we don't error out these pending add ops, they would be
// leak and never callback.
//
// The race condition happen in following sequence:
// a) ledger L is fenced
// b) write entry E encountered LedgerFencedException, trigger ledger close procedure
// c) ledger close encountered metadata version exception and set ledger metadata back to open
// d) writer tries to write entry E+1, since ledger metadata is still open (reset by c))
// e) the close procedure in c) resolved the metadata conflicts and set ledger metadata to closed
// f) writing entry E+1 encountered LedgerFencedException which will enter ledger close procedure
// g) it would find that ledger metadata is closed, then it callbacks immediately without erroring out any pendings
synchronized (LedgerHandle.this) {
pendingAdds = drainPendingAddsToErrorOut();
}
errorOutPendingAdds(rc, pendingAdds);
cb.closeComplete(BKException.Code.OK, LedgerHandle.this, ctx);
return;
}
synchronized(LedgerHandle.this) {
prevState = metadata.getState();
prevLastEntryId = metadata.getLastEntryId();
prevLength = metadata.getLength();
// drain pending adds first
pendingAdds = drainPendingAddsToErrorOut();
// synchronized on LedgerHandle.this to ensure that
// lastAddPushed can not be updated after the metadata
// is closed.
metadata.setLength(length);
metadata.close(lastAddConfirmed);
lastAddPushed = lastAddConfirmed;
}
// error out all pending adds during closing, the callbacks shouldn't be
// running under any bk locks.
errorOutPendingAdds(rc, pendingAdds);
if (LOG.isDebugEnabled()) {
LOG.debug("Closing ledger: " + ledgerId + " at entryId: "
+ metadata.getLastEntryId() + " with this many bytes: " + metadata.getLength());
}
final class CloseCb extends OrderedSafeGenericCallback {
CloseCb() {
super(bk.mainWorkerPool, ledgerId);
}
@Override
public void safeOperationComplete(final int rc, Void result) {
if (rc == BKException.Code.MetadataVersionException) {
rereadMetadata(new OrderedSafeGenericCallback(bk.mainWorkerPool,
ledgerId) {
@Override
public void safeOperationComplete(int newrc, LedgerMetadata newMeta) {
if (newrc != BKException.Code.OK) {
LOG.error("Error reading new metadata from ledger " + ledgerId
+ " when closing, code=" + newrc);
cb.closeComplete(rc, LedgerHandle.this, ctx);
} else {
metadata.setState(prevState);
if (prevState.equals(State.CLOSED)) {
metadata.close(prevLastEntryId);
}
metadata.setLength(prevLength);
if (!metadata.isNewerThan(newMeta)
&& !metadata.isConflictWith(newMeta)) {
// use the new metadata's ensemble, in case re-replication already
// replaced some bookies in the ensemble.
metadata.setEnsembles(newMeta.getEnsembles());
metadata.setVersion(newMeta.version);
metadata.setLength(length);
metadata.close(lastAddConfirmed);
writeLedgerConfig(new CloseCb());
return;
} else {
metadata.setLength(length);
metadata.close(lastAddConfirmed);
LOG.warn("Conditional update ledger metadata for ledger " + ledgerId + " failed.");
cb.closeComplete(rc, LedgerHandle.this, ctx);
}
}
}
});
} else if (rc != BKException.Code.OK) {
LOG.error("Error update ledger metadata for ledger " + ledgerId + " : " + rc);
cb.closeComplete(rc, LedgerHandle.this, ctx);
} else {
cb.closeComplete(BKException.Code.OK, LedgerHandle.this, ctx);
}
}
};
writeLedgerConfig(new CloseCb());
}
});
}
/**
* Read a sequence of entries synchronously.
*
* @param firstEntry
* id of first entry of sequence (included)
* @param lastEntry
* id of last entry of sequence (included)
*
*/
public Enumeration readEntries(long firstEntry, long lastEntry)
throws InterruptedException, BKException {
SyncCounter counter = new SyncCounter();
counter.inc();
asyncReadEntries(firstEntry, lastEntry, new SyncReadCallback(), counter);
counter.block(0);
if (counter.getrc() != BKException.Code.OK) {
throw BKException.create(counter.getrc());
}
return counter.getSequence();
}
/**
* Read a sequence of entries asynchronously.
*
* @param firstEntry
* id of first entry of sequence
* @param lastEntry
* id of last entry of sequence
* @param cb
* object implementing read callback interface
* @param ctx
* control object
*/
public void asyncReadEntries(long firstEntry, long lastEntry,
ReadCallback cb, Object ctx) {
// Little sanity check
if (firstEntry < 0 || lastEntry > lastAddConfirmed
|| firstEntry > lastEntry) {
cb.readComplete(BKException.Code.ReadException, this, null, ctx);
return;
}
try {
new PendingReadOp(this, bk.scheduler,
firstEntry, lastEntry, cb, ctx).initiate();
} catch (InterruptedException e) {
cb.readComplete(BKException.Code.InterruptedException, this, null, ctx);
}
}
/**
* Add entry synchronously to an open ledger.
*
* @param data
* array of bytes to be written to the ledger
* @return the entryId of the new inserted entry
*/
public long addEntry(byte[] data) throws InterruptedException, BKException {
return addEntry(data, 0, data.length);
}
/**
* Add entry synchronously to an open ledger.
*
* @param data
* array of bytes to be written to the ledger
* @param offset
* offset from which to take bytes from data
* @param length
* number of bytes to take from data
* @return the entryId of the new inserted entry
*/
public long addEntry(byte[] data, int offset, int length)
throws InterruptedException, BKException {
LOG.debug("Adding entry {}", data);
SyncCounter counter = new SyncCounter();
counter.inc();
SyncAddCallback callback = new SyncAddCallback();
asyncAddEntry(data, offset, length, callback, counter);
counter.block(0);
if (counter.getrc() != BKException.Code.OK) {
throw BKException.create(counter.getrc());
}
return callback.entryId;
}
/**
* Add entry asynchronously to an open ledger.
*
* @param data
* array of bytes to be written
* @param cb
* object implementing callbackinterface
* @param ctx
* some control object
*/
public void asyncAddEntry(final byte[] data, final AddCallback cb,
final Object ctx) {
asyncAddEntry(data, 0, data.length, cb, ctx);
}
/**
* Add entry asynchronously to an open ledger, using an offset and range.
*
* @param data
* array of bytes to be written
* @param offset
* offset from which to take bytes from data
* @param length
* number of bytes to take from data
* @param cb
* object implementing callbackinterface
* @param ctx
* some control object
* @throws ArrayIndexOutOfBoundsException if offset or length is negative or
* offset and length sum to a value higher than the length of data.
*/
public void asyncAddEntry(final byte[] data, final int offset, final int length,
final AddCallback cb, final Object ctx) {
PendingAddOp op = new PendingAddOp(LedgerHandle.this, cb, ctx);
doAsyncAddEntry(op, data, offset, length, cb, ctx);
}
/**
* Make a recovery add entry request. Recovery adds can add to a ledger even if
* it has been fenced.
*
* This is only valid for bookie and ledger recovery, which may need to replicate
* entries to a quorum of bookies to ensure data safety.
*
* Normal client should never call this method.
*/
void asyncRecoveryAddEntry(final byte[] data, final int offset, final int length,
final AddCallback cb, final Object ctx) {
PendingAddOp op = new PendingAddOp(LedgerHandle.this, cb, ctx).enableRecoveryAdd();
doAsyncAddEntry(op, data, offset, length, cb, ctx);
}
private void doAsyncAddEntry(final PendingAddOp op, final byte[] data, final int offset, final int length,
final AddCallback cb, final Object ctx) {
if (offset < 0 || length < 0
|| (offset + length) > data.length) {
throw new ArrayIndexOutOfBoundsException(
"Invalid values for offset("+offset
+") or length("+length+")");
}
throttler.acquire();
final long entryId;
final long currentLength;
boolean wasClosed = false;
synchronized(this) {
// synchronized on this to ensure that
// the ledger isn't closed between checking and
// updating lastAddPushed
if (metadata.isClosed()) {
wasClosed = true;
entryId = -1;
currentLength = 0;
} else {
entryId = ++lastAddPushed;
currentLength = addToLength(length);
op.setEntryId(entryId);
pendingAddOps.add(op);
}
}
if (wasClosed) {
// make sure the callback is triggered in main worker pool
try {
bk.mainWorkerPool.submit(new SafeRunnable() {
@Override
public void safeRun() {
LOG.warn("Attempt to add to closed ledger: {}", ledgerId);
cb.addComplete(BKException.Code.LedgerClosedException,
LedgerHandle.this, INVALID_ENTRY_ID, ctx);
}
@Override
public String toString() {
return String.format("AsyncAddEntryToClosedLedger(lid=%d)", ledgerId);
}
});
} catch (RejectedExecutionException e) {
cb.addComplete(BKException.Code.InterruptedException,
LedgerHandle.this, INVALID_ENTRY_ID, ctx);
}
return;
}
try {
bk.mainWorkerPool.submit(new SafeRunnable() {
@Override
public void safeRun() {
ChannelBuffer toSend = macManager.computeDigestAndPackageForSending(
entryId, lastAddConfirmed, currentLength, data, offset, length);
op.initiate(toSend, length);
}
});
} catch (RuntimeException e) {
cb.addComplete(BKException.Code.InterruptedException,
LedgerHandle.this, INVALID_ENTRY_ID, ctx);
}
}
/**
* Obtains asynchronously the last confirmed write from a quorum of bookies. This
* call obtains the the last add confirmed each bookie has received for this ledger
* and returns the maximum. If the ledger has been closed, the value returned by this
* call may not correspond to the id of the last entry of the ledger, since it reads
* the hint of bookies. Consequently, in the case the ledger has been closed, it may
* return a different value than getLastAddConfirmed, which returns the local value
* of the ledger handle.
*
* @see #getLastAddConfirmed()
*
* @param cb
* @param ctx
*/
public void asyncReadLastConfirmed(final ReadLastConfirmedCallback cb, final Object ctx) {
boolean isClosed;
long lastEntryId;
synchronized (this) {
isClosed = metadata.isClosed();
lastEntryId = metadata.getLastEntryId();
}
if (isClosed) {
cb.readLastConfirmedComplete(BKException.Code.OK, lastEntryId, ctx);
return;
}
ReadLastConfirmedOp.LastConfirmedDataCallback innercb = new ReadLastConfirmedOp.LastConfirmedDataCallback() {
@Override
public void readLastConfirmedDataComplete(int rc, DigestManager.RecoveryData data) {
if (rc == BKException.Code.OK) {
lastAddConfirmed = Math.max(lastAddConfirmed, data.lastAddConfirmed);
lastAddPushed = Math.max(lastAddPushed, data.lastAddConfirmed);
length = Math.max(length, data.length);
cb.readLastConfirmedComplete(rc, data.lastAddConfirmed, ctx);
} else {
cb.readLastConfirmedComplete(rc, INVALID_ENTRY_ID, ctx);
}
}
};
new ReadLastConfirmedOp(this, innercb).initiate();
}
/**
* Context objects for synchronous call to read last confirmed.
*/
static class LastConfirmedCtx {
final static long ENTRY_ID_PENDING = -10;
long response;
int rc;
LastConfirmedCtx() {
this.response = ENTRY_ID_PENDING;
}
void setLastConfirmed(long lastConfirmed) {
this.response = lastConfirmed;
}
long getlastConfirmed() {
return this.response;
}
void setRC(int rc) {
this.rc = rc;
}
int getRC() {
return this.rc;
}
boolean ready() {
return (this.response != ENTRY_ID_PENDING);
}
}
/**
* Obtains synchronously the last confirmed write from a quorum of bookies. This call
* obtains the the last add confirmed each bookie has received for this ledger
* and returns the maximum. If the ledger has been closed, the value returned by this
* call may not correspond to the id of the last entry of the ledger, since it reads
* the hint of bookies. Consequently, in the case the ledger has been closed, it may
* return a different value than getLastAddConfirmed, which returns the local value
* of the ledger handle.
*
* @see #getLastAddConfirmed()
*
* @return The entry id of the last confirmed write or {@link #INVALID_ENTRY_ID INVALID_ENTRY_ID}
* if no entry has been confirmed
* @throws InterruptedException
* @throws BKException
*/
public long readLastConfirmed()
throws InterruptedException, BKException {
LastConfirmedCtx ctx = new LastConfirmedCtx();
asyncReadLastConfirmed(new SyncReadLastConfirmedCallback(), ctx);
synchronized(ctx) {
while(!ctx.ready()) {
ctx.wait();
}
}
if(ctx.getRC() != BKException.Code.OK) throw BKException.create(ctx.getRC());
return ctx.getlastConfirmed();
}
// close the ledger and send fails to all the adds in the pipeline
void handleUnrecoverableErrorDuringAdd(int rc) {
if (metadata.isInRecovery()) {
// we should not close ledger if ledger is recovery mode
// otherwise we may lose entry.
errorOutPendingAdds(rc);
return;
}
LOG.error("Closing ledger {} due to error {}", ledgerId, rc);
asyncCloseInternal(NoopCloseCallback.instance, null, rc);
}
void errorOutPendingAdds(int rc) {
errorOutPendingAdds(rc, drainPendingAddsToErrorOut());
}
synchronized List drainPendingAddsToErrorOut() {
PendingAddOp pendingAddOp;
List opsDrained = new ArrayList(pendingAddOps.size());
while ((pendingAddOp = pendingAddOps.poll()) != null) {
addToLength(-pendingAddOp.entryLength);
opsDrained.add(pendingAddOp);
}
return opsDrained;
}
void errorOutPendingAdds(int rc, List ops) {
for (PendingAddOp op : ops) {
op.submitCallback(rc);
}
}
void sendAddSuccessCallbacks() {
// Start from the head of the queue and proceed while there are
// entries that have had all their responses come back
PendingAddOp pendingAddOp;
while ((pendingAddOp = pendingAddOps.peek()) != null
&& blockAddCompletions.get() == 0) {
if (!pendingAddOp.completed) {
return;
}
pendingAddOps.remove();
lastAddConfirmed = pendingAddOp.entryId;
pendingAddOp.submitCallback(BKException.Code.OK);
}
}
ArrayList replaceBookieInMetadata(final InetSocketAddress addr, final int bookieIndex)
throws BKException.BKNotEnoughBookiesException {
InetSocketAddress newBookie;
LOG.info("Handling failure of bookie: {} index: {}", addr, bookieIndex);
final ArrayList newEnsemble = new ArrayList();
final long newEnsembleStartEntry = lastAddConfirmed + 1;
// avoid parallel ensemble changes to same ensemble.
synchronized (metadata) {
newBookie = bk.bookieWatcher.getAdditionalBookie(metadata.currentEnsemble);
newEnsemble.addAll(metadata.currentEnsemble);
newEnsemble.set(bookieIndex, newBookie);
if (LOG.isDebugEnabled()) {
LOG.debug("Changing ensemble from: " + metadata.currentEnsemble
+ " to: " + newEnsemble + " for ledger: " + ledgerId
+ " starting at entry: " + (lastAddConfirmed + 1));
}
metadata.addEnsemble(newEnsembleStartEntry, newEnsemble);
}
return newEnsemble;
}
void handleBookieFailure(final InetSocketAddress addr, final int bookieIndex) {
blockAddCompletions.incrementAndGet();
synchronized (metadata) {
if (!metadata.currentEnsemble.get(bookieIndex).equals(addr)) {
// ensemble has already changed, failure of this addr is immaterial
LOG.warn("Write did not succeed to {}, bookieIndex {}, but we have already fixed it.",
addr, bookieIndex);
blockAddCompletions.decrementAndGet();
return;
}
try {
ArrayList newEnsemble = replaceBookieInMetadata(addr, bookieIndex);
EnsembleInfo ensembleInfo = new EnsembleInfo(newEnsemble, bookieIndex,
addr);
writeLedgerConfig(new ChangeEnsembleCb(ensembleInfo));
} catch (BKException.BKNotEnoughBookiesException e) {
LOG.error("Could not get additional bookie to "
+ "remake ensemble, closing ledger: " + ledgerId);
handleUnrecoverableErrorDuringAdd(e.getCode());
return;
}
}
}
// Contains newly reformed ensemble, bookieIndex, failedBookieAddress
private static final class EnsembleInfo {
private final ArrayList newEnsemble;
private final int bookieIndex;
private final InetSocketAddress addr;
public EnsembleInfo(ArrayList newEnsemble,
int bookieIndex, InetSocketAddress addr) {
this.newEnsemble = newEnsemble;
this.bookieIndex = bookieIndex;
this.addr = addr;
}
}
/**
* Callback which is updating the ledgerMetadata in zk with the newly
* reformed ensemble. On MetadataVersionException, will reread latest
* ledgerMetadata and act upon.
*/
private final class ChangeEnsembleCb extends OrderedSafeGenericCallback {
private final EnsembleInfo ensembleInfo;
ChangeEnsembleCb(EnsembleInfo ensembleInfo) {
super(bk.mainWorkerPool, ledgerId);
this.ensembleInfo = ensembleInfo;
}
@Override
public void safeOperationComplete(final int rc, Void result) {
if (rc == BKException.Code.MetadataVersionException) {
rereadMetadata(new ReReadLedgerMetadataCb(rc,
ensembleInfo));
return;
} else if (rc != BKException.Code.OK) {
LOG.error("Could not persist ledger metadata while "
+ "changing ensemble to: "
+ ensembleInfo.newEnsemble
+ " , closing ledger");
handleUnrecoverableErrorDuringAdd(rc);
return;
}
blockAddCompletions.decrementAndGet();
// the failed bookie has been replaced
unsetSuccessAndSendWriteRequest(ensembleInfo.bookieIndex);
}
};
/**
* Callback which is reading the ledgerMetadata present in zk. This will try
* to resolve the version conflicts.
*/
private final class ReReadLedgerMetadataCb extends OrderedSafeGenericCallback {
private final int rc;
private final EnsembleInfo ensembleInfo;
ReReadLedgerMetadataCb(int rc, EnsembleInfo ensembleInfo) {
super(bk.mainWorkerPool, ledgerId);
this.rc = rc;
this.ensembleInfo = ensembleInfo;
}
@Override
public void safeOperationComplete(int newrc, LedgerMetadata newMeta) {
if (newrc != BKException.Code.OK) {
LOG.error("Error reading new metadata from ledger "
+ "after changing ensemble, code=" + newrc);
handleUnrecoverableErrorDuringAdd(rc);
} else {
if (!resolveConflict(newMeta)) {
LOG.error("Could not resolve ledger metadata conflict "
+ "while changing ensemble to: "
+ ensembleInfo.newEnsemble
+ ", old meta data is \n"
+ new String(metadata.serialize())
+ "\n, new meta data is \n"
+ new String(newMeta.serialize())
+ "\n ,closing ledger");
handleUnrecoverableErrorDuringAdd(rc);
}
}
}
/**
* Specific resolve conflicts happened when multiple bookies failures in same ensemble.
*
* Resolving the version conflicts between local ledgerMetadata and zk
* ledgerMetadata. This will do the following:
*
* -
* check whether ledgerMetadata state matches of local and zk
* -
* if the zk ledgerMetadata still contains the failed bookie, then
* update zookeeper with the newBookie otherwise send write request
*
*
*/
private boolean resolveConflict(LedgerMetadata newMeta) {
// make sure the ledger isn't closed by other ones.
if (metadata.getState() != newMeta.getState()) {
return false;
}
// We should check number of ensembles since there are two kinds of metadata conflicts:
// - Case 1: Multiple bookies involved in ensemble change.
// Number of ensembles should be same in this case.
// - Case 2: Recovery (Auto/Manually) replaced ensemble and ensemble changed.
// The metadata changed due to ensemble change would have one more ensemble
// than the metadata changed by recovery.
int diff = newMeta.getEnsembles().size() - metadata.getEnsembles().size();
if (0 != diff) {
if (-1 == diff) {
// Case 1: metadata is changed by other ones (e.g. Recovery)
return updateMetadataIfPossible(newMeta);
}
return false;
}
//
// Case 2:
//
// If the failed the bookie is still existed in the metadata (in zookeeper), it means that
// the ensemble change of the failed bookie is failed due to metadata conflicts. so try to
// update the ensemble change metadata again. Otherwise, it means that the ensemble change
// is already succeed, unset the success and re-adding entries.
if (newMeta.currentEnsemble.get(ensembleInfo.bookieIndex).equals(
ensembleInfo.addr)) {
// If the in-memory data doesn't contains the failed bookie, it means the ensemble change
// didn't finish, so try to resolve conflicts with the metadata read from zookeeper and
// update ensemble changed metadata again.
if (!metadata.currentEnsemble.get(ensembleInfo.bookieIndex)
.equals(ensembleInfo.addr)) {
return updateMetadataIfPossible(newMeta);
}
} else {
// the failed bookie has been replaced
blockAddCompletions.decrementAndGet();
unsetSuccessAndSendWriteRequest(ensembleInfo.bookieIndex);
}
return true;
}
private boolean updateMetadataIfPossible(LedgerMetadata newMeta) {
// if the local metadata is newer than zookeeper metadata, it means that metadata is updated
// again when it was trying re-reading the metatada, re-kick the reread again
if (metadata.isNewerThan(newMeta)) {
rereadMetadata(this);
return true;
}
// make sure the metadata doesn't changed by other ones.
if (metadata.isConflictWith(newMeta)) {
return false;
}
LOG.info("Resolve ledger metadata conflict while changing ensemble to: {},"
+ " old meta data is \n {} \n, new meta data is \n {}.", new Object[] {
ensembleInfo.newEnsemble, metadata, newMeta });
// update znode version
metadata.setVersion(newMeta.getVersion());
// merge ensemble infos from new meta except last ensemble
// since they might be modified by recovery tool.
metadata.mergeEnsembles(newMeta.getEnsembles());
writeLedgerConfig(new ChangeEnsembleCb(ensembleInfo));
return true;
}
};
void unsetSuccessAndSendWriteRequest(final int bookieIndex) {
for (PendingAddOp pendingAddOp : pendingAddOps) {
pendingAddOp.unsetSuccessAndSendWriteRequest(bookieIndex);
}
}
void rereadMetadata(final GenericCallback cb) {
bk.getLedgerManager().readLedgerMetadata(ledgerId, cb);
}
void recover(final GenericCallback cb) {
boolean wasClosed = false;
boolean wasInRecovery = false;
synchronized (this) {
if (metadata.isClosed()) {
lastAddConfirmed = lastAddPushed = metadata.getLastEntryId();
length = metadata.getLength();
wasClosed = true;
} else {
wasClosed = false;
if (metadata.isInRecovery()) {
wasInRecovery = true;
} else {
wasInRecovery = false;
metadata.markLedgerInRecovery();
}
}
}
if (wasClosed) {
// We are already closed, nothing to do
cb.operationComplete(BKException.Code.OK, null);
return;
}
if (wasInRecovery) {
// if metadata is already in recover, dont try to write again,
// just do the recovery from the starting point
new LedgerRecoveryOp(LedgerHandle.this, cb).initiate();
return;
}
writeLedgerConfig(new OrderedSafeGenericCallback(bk.mainWorkerPool, ledgerId) {
@Override
public void safeOperationComplete(final int rc, Void result) {
if (rc == BKException.Code.MetadataVersionException) {
rereadMetadata(new OrderedSafeGenericCallback(bk.mainWorkerPool,
ledgerId) {
@Override
public void safeOperationComplete(int rc, LedgerMetadata newMeta) {
if (rc != BKException.Code.OK) {
cb.operationComplete(rc, null);
} else {
metadata = newMeta;
recover(cb);
}
}
});
} else if (rc == BKException.Code.OK) {
new LedgerRecoveryOp(LedgerHandle.this, cb).initiate();
} else {
LOG.error("Error writing ledger config " + rc + " of ledger " + ledgerId);
cb.operationComplete(rc, null);
}
}
});
}
static class NoopCloseCallback implements CloseCallback {
static NoopCloseCallback instance = new NoopCloseCallback();
@Override
public void closeComplete(int rc, LedgerHandle lh, Object ctx) {
if (rc != BKException.Code.OK) {
LOG.warn("Close failed: " + BKException.getMessage(rc));
}
// noop
}
}
private static class SyncReadCallback implements ReadCallback {
/**
* Implementation of callback interface for synchronous read method.
*
* @param rc
* return code
* @param leder
* ledger identifier
* @param seq
* sequence of entries
* @param ctx
* control object
*/
@Override
public void readComplete(int rc, LedgerHandle lh,
Enumeration seq, Object ctx) {
SyncCounter counter = (SyncCounter) ctx;
synchronized (counter) {
counter.setSequence(seq);
counter.setrc(rc);
counter.dec();
counter.notify();
}
}
}
private static class SyncAddCallback implements AddCallback {
long entryId = -1;
/**
* Implementation of callback interface for synchronous read method.
*
* @param rc
* return code
* @param leder
* ledger identifier
* @param entry
* entry identifier
* @param ctx
* control object
*/
@Override
public void addComplete(int rc, LedgerHandle lh, long entry, Object ctx) {
SyncCounter counter = (SyncCounter) ctx;
this.entryId = entry;
counter.setrc(rc);
counter.dec();
}
}
private static class SyncReadLastConfirmedCallback implements ReadLastConfirmedCallback {
/**
* Implementation of callback interface for synchronous read last confirmed method.
*/
@Override
public void readLastConfirmedComplete(int rc, long lastConfirmed, Object ctx) {
LastConfirmedCtx lcCtx = (LastConfirmedCtx) ctx;
synchronized(lcCtx) {
lcCtx.setRC(rc);
lcCtx.setLastConfirmed(lastConfirmed);
lcCtx.notify();
}
}
}
private static class SyncCloseCallback implements CloseCallback {
/**
* Close callback method
*
* @param rc
* @param lh
* @param ctx
*/
@Override
public void closeComplete(int rc, LedgerHandle lh, Object ctx) {
SyncCounter counter = (SyncCounter) ctx;
counter.setrc(rc);
synchronized (counter) {
counter.dec();
counter.notify();
}
}
}
}
LedgerMetadata.java 0000664 0000000 0000000 00000044764 12445073612 0034441 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/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.
*/
package org.apache.bookkeeper.client;
import static com.google.common.base.Charsets.UTF_8;
import java.io.BufferedReader;
import java.io.StringReader;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Arrays;
import org.apache.bookkeeper.versioning.Version;
import com.google.protobuf.TextFormat;
import com.google.protobuf.ByteString;
import org.apache.bookkeeper.proto.DataFormats.LedgerMetadataFormat;
import org.apache.bookkeeper.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class encapsulates all the ledger metadata that is persistently stored
* in zookeeper. It provides parsing and serialization methods of such metadata.
*
*/
public class LedgerMetadata {
static final Logger LOG = LoggerFactory.getLogger(LedgerMetadata.class);
private static final String closed = "CLOSED";
private static final String lSplitter = "\n";
private static final String tSplitter = "\t";
// can't use -1 for NOTCLOSED because that is reserved for a closed, empty
// ledger
private static final int NOTCLOSED = -101;
private static final int IN_RECOVERY = -102;
public static final int LOWEST_COMPAT_METADATA_FORMAT_VERSION = 0;
public static final int CURRENT_METADATA_FORMAT_VERSION = 2;
public static final String VERSION_KEY = "BookieMetadataFormatVersion";
private int metadataFormatVersion = 0;
private int ensembleSize;
private int writeQuorumSize;
private int ackQuorumSize;
private long length;
private long lastEntryId;
private LedgerMetadataFormat.State state;
private SortedMap> ensembles = new TreeMap>();
ArrayList currentEnsemble;
volatile Version version = Version.NEW;
private boolean hasPassword = false;
private LedgerMetadataFormat.DigestType digestType;
private byte[] password;
public LedgerMetadata(int ensembleSize, int writeQuorumSize, int ackQuorumSize,
BookKeeper.DigestType digestType, byte[] password) {
this.ensembleSize = ensembleSize;
this.writeQuorumSize = writeQuorumSize;
this.ackQuorumSize = ackQuorumSize;
/*
* It is set in PendingReadOp.readEntryComplete, and
* we read it in LedgerRecoveryOp.readComplete.
*/
this.length = 0;
this.state = LedgerMetadataFormat.State.OPEN;
this.lastEntryId = LedgerHandle.INVALID_ENTRY_ID;
this.metadataFormatVersion = CURRENT_METADATA_FORMAT_VERSION;
this.digestType = digestType.equals(BookKeeper.DigestType.MAC) ?
LedgerMetadataFormat.DigestType.HMAC : LedgerMetadataFormat.DigestType.CRC32;
this.password = Arrays.copyOf(password, password.length);
this.hasPassword = true;
}
/**
* Copy Constructor.
*/
LedgerMetadata(LedgerMetadata other) {
this.ensembleSize = other.ensembleSize;
this.writeQuorumSize = other.writeQuorumSize;
this.ackQuorumSize = other.ackQuorumSize;
this.length = other.length;
this.lastEntryId = other.lastEntryId;
this.metadataFormatVersion = other.metadataFormatVersion;
this.state = other.state;
this.version = other.version;
this.hasPassword = other.hasPassword;
this.digestType = other.digestType;
this.password = new byte[other.password.length];
System.arraycopy(other.password, 0, this.password, 0, other.password.length);
// copy the ensembles
for (Entry> entry : other.ensembles.entrySet()) {
long startEntryId = entry.getKey();
ArrayList newEnsemble = new ArrayList(entry.getValue());
this.addEnsemble(startEntryId, newEnsemble);
}
}
private LedgerMetadata() {
this(0, 0, 0, BookKeeper.DigestType.MAC, new byte[] {});
this.hasPassword = false;
}
/**
* Get the Map of bookie ensembles for the various ledger fragments
* that make up the ledger.
*
* @return SortedMap of Ledger Fragments and the corresponding
* bookie ensembles that store the entries.
*/
public SortedMap> getEnsembles() {
return ensembles;
}
void setEnsembles(SortedMap> ensembles) {
this.ensembles = ensembles;
}
public int getEnsembleSize() {
return ensembleSize;
}
public int getWriteQuorumSize() {
return writeQuorumSize;
}
public int getAckQuorumSize() {
return ackQuorumSize;
}
/**
* In versions 4.1.0 and below, the digest type and password were not
* stored in the metadata.
*
* @return whether the password has been stored in the metadata
*/
boolean hasPassword() {
return hasPassword;
}
byte[] getPassword() {
return Arrays.copyOf(password, password.length);
}
BookKeeper.DigestType getDigestType() {
if (digestType.equals(LedgerMetadataFormat.DigestType.HMAC)) {
return BookKeeper.DigestType.MAC;
} else {
return BookKeeper.DigestType.CRC32;
}
}
public long getLastEntryId() {
return lastEntryId;
}
public long getLength() {
return length;
}
void setLength(long length) {
this.length = length;
}
public boolean isClosed() {
return state == LedgerMetadataFormat.State.CLOSED;
}
public boolean isInRecovery() {
return state == LedgerMetadataFormat.State.IN_RECOVERY;
}
LedgerMetadataFormat.State getState() {
return state;
}
void setState(LedgerMetadataFormat.State state) {
this.state = state;
}
void markLedgerInRecovery() {
state = LedgerMetadataFormat.State.IN_RECOVERY;
}
void close(long entryId) {
lastEntryId = entryId;
state = LedgerMetadataFormat.State.CLOSED;
}
void addEnsemble(long startEntryId, ArrayList ensemble) {
assert ensembles.isEmpty() || startEntryId >= ensembles.lastKey();
ensembles.put(startEntryId, ensemble);
currentEnsemble = ensemble;
}
ArrayList getEnsemble(long entryId) {
// the head map cannot be empty, since we insert an ensemble for
// entry-id 0, right when we start
return ensembles.get(ensembles.headMap(entryId + 1).lastKey());
}
/**
* the entry id > the given entry-id at which the next ensemble change takes
* place ( -1 if no further ensemble changes)
*
* @param entryId
* @return
*/
long getNextEnsembleChange(long entryId) {
SortedMap> tailMap = ensembles.tailMap(entryId + 1);
if (tailMap.isEmpty()) {
return -1;
} else {
return tailMap.firstKey();
}
}
/**
* Generates a byte array of this object
*
* @return the metadata serialized into a byte array
*/
public byte[] serialize() {
if (metadataFormatVersion == 1) {
return serializeVersion1();
}
LedgerMetadataFormat.Builder builder = LedgerMetadataFormat.newBuilder();
builder.setQuorumSize(writeQuorumSize).setAckQuorumSize(ackQuorumSize)
.setEnsembleSize(ensembleSize).setLength(length)
.setState(state).setLastEntryId(lastEntryId);
if (hasPassword) {
builder.setDigestType(digestType).setPassword(ByteString.copyFrom(password));
}
for (Map.Entry> entry : ensembles.entrySet()) {
LedgerMetadataFormat.Segment.Builder segmentBuilder = LedgerMetadataFormat.Segment.newBuilder();
segmentBuilder.setFirstEntryId(entry.getKey());
for (InetSocketAddress addr : entry.getValue()) {
segmentBuilder.addEnsembleMember(StringUtils.addrToString(addr));
}
builder.addSegment(segmentBuilder.build());
}
StringBuilder s = new StringBuilder();
s.append(VERSION_KEY).append(tSplitter).append(CURRENT_METADATA_FORMAT_VERSION).append(lSplitter);
s.append(TextFormat.printToString(builder.build()));
LOG.debug("Serialized config: {}", s);
return s.toString().getBytes();
}
private byte[] serializeVersion1() {
StringBuilder s = new StringBuilder();
s.append(VERSION_KEY).append(tSplitter).append(metadataFormatVersion).append(lSplitter);
s.append(writeQuorumSize).append(lSplitter).append(ensembleSize).append(lSplitter).append(length);
for (Map.Entry> entry : ensembles.entrySet()) {
s.append(lSplitter).append(entry.getKey());
for (InetSocketAddress addr : entry.getValue()) {
s.append(tSplitter);
s.append(StringUtils.addrToString(addr));
}
}
if (state == LedgerMetadataFormat.State.IN_RECOVERY) {
s.append(lSplitter).append(IN_RECOVERY).append(tSplitter).append(closed);
} else if (state == LedgerMetadataFormat.State.CLOSED) {
s.append(lSplitter).append(getLastEntryId()).append(tSplitter).append(closed);
}
LOG.debug("Serialized config: {}", s);
return s.toString().getBytes();
}
/**
* Parses a given byte array and transforms into a LedgerConfig object
*
* @param bytes
* byte array to parse
* @param version
* version of the ledger metadata
* @return LedgerConfig
* @throws IOException
* if the given byte[] cannot be parsed
*/
public static LedgerMetadata parseConfig(byte[] bytes, Version version) throws IOException {
LedgerMetadata lc = new LedgerMetadata();
lc.version = version;
String config = new String(bytes);
LOG.debug("Parsing Config: {}", config);
BufferedReader reader = new BufferedReader(new StringReader(config));
String versionLine = reader.readLine();
if (versionLine == null) {
throw new IOException("Invalid metadata. Content missing");
}
int i = 0;
if (versionLine.startsWith(VERSION_KEY)) {
String parts[] = versionLine.split(tSplitter);
lc.metadataFormatVersion = new Integer(parts[1]);
} else {
// if no version is set, take it to be version 1
// as the parsing is the same as what we had before
// we introduce versions
lc.metadataFormatVersion = 1;
// reset the reader
reader.close();
reader = new BufferedReader(new StringReader(config));
}
if (lc.metadataFormatVersion < LOWEST_COMPAT_METADATA_FORMAT_VERSION
|| lc.metadataFormatVersion > CURRENT_METADATA_FORMAT_VERSION) {
throw new IOException("Metadata version not compatible. Expected between "
+ LOWEST_COMPAT_METADATA_FORMAT_VERSION + " and " + CURRENT_METADATA_FORMAT_VERSION
+ ", but got " + lc.metadataFormatVersion);
}
if (lc.metadataFormatVersion == 1) {
return parseVersion1Config(lc, reader);
}
LedgerMetadataFormat.Builder builder = LedgerMetadataFormat.newBuilder();
TextFormat.merge(reader, builder);
LedgerMetadataFormat data = builder.build();
lc.writeQuorumSize = data.getQuorumSize();
if (data.hasAckQuorumSize()) {
lc.ackQuorumSize = data.getAckQuorumSize();
} else {
lc.ackQuorumSize = lc.writeQuorumSize;
}
lc.ensembleSize = data.getEnsembleSize();
lc.length = data.getLength();
lc.state = data.getState();
lc.lastEntryId = data.getLastEntryId();
if (data.hasPassword()) {
lc.digestType = data.getDigestType();
lc.password = data.getPassword().toByteArray();
lc.hasPassword = true;
}
for (LedgerMetadataFormat.Segment s : data.getSegmentList()) {
ArrayList addrs = new ArrayList();
for (String member : s.getEnsembleMemberList()) {
addrs.add(StringUtils.parseAddr(member));
}
lc.addEnsemble(s.getFirstEntryId(), addrs);
}
return lc;
}
static LedgerMetadata parseVersion1Config(LedgerMetadata lc,
BufferedReader reader) throws IOException {
try {
lc.writeQuorumSize = lc.ackQuorumSize = new Integer(reader.readLine());
lc.ensembleSize = new Integer(reader.readLine());
lc.length = new Long(reader.readLine());
String line = reader.readLine();
while (line != null) {
String parts[] = line.split(tSplitter);
if (parts[1].equals(closed)) {
Long l = new Long(parts[0]);
if (l == IN_RECOVERY) {
lc.state = LedgerMetadataFormat.State.IN_RECOVERY;
} else {
lc.state = LedgerMetadataFormat.State.CLOSED;
lc.lastEntryId = l;
}
break;
} else {
lc.state = LedgerMetadataFormat.State.OPEN;
}
ArrayList addrs = new ArrayList();
for (int j = 1; j < parts.length; j++) {
addrs.add(StringUtils.parseAddr(parts[j]));
}
lc.addEnsemble(new Long(parts[0]), addrs);
line = reader.readLine();
}
} catch (NumberFormatException e) {
throw new IOException(e);
}
return lc;
}
/**
* Updates the version of this metadata.
*
* @param v Version
*/
public void setVersion(Version v) {
this.version = v;
}
/**
* Returns the last version.
*
* @return version
*/
public Version getVersion() {
return this.version;
}
/**
* Is the metadata newer that given newMeta.
*
* @param newMeta
* @return
*/
boolean isNewerThan(LedgerMetadata newMeta) {
if (null == version) {
return false;
}
return Version.Occurred.AFTER == version.compare(newMeta.version);
}
/**
* Is the metadata conflict with new updated metadata.
*
* @param newMeta
* Re-read metadata
* @return true if the metadata is conflict.
*/
boolean isConflictWith(LedgerMetadata newMeta) {
/*
* if length & close have changed, then another client has
* opened the ledger, can't resolve this conflict.
*/
if (metadataFormatVersion != newMeta.metadataFormatVersion ||
ensembleSize != newMeta.ensembleSize ||
writeQuorumSize != newMeta.writeQuorumSize ||
ackQuorumSize != newMeta.ackQuorumSize ||
length != newMeta.length ||
state != newMeta.state ||
!digestType.equals(newMeta.digestType) ||
!Arrays.equals(password, newMeta.password)) {
return true;
}
if (state == LedgerMetadataFormat.State.CLOSED
&& lastEntryId != newMeta.lastEntryId) {
return true;
}
// if ledger is closed, we can just take the new ensembles
if (newMeta.state != LedgerMetadataFormat.State.CLOSED) {
// allow new metadata to be one ensemble less than current metadata
// since ensemble change might kick in when recovery changed metadata
int diff = ensembles.size() - newMeta.ensembles.size();
if (0 != diff && 1 != diff) {
return true;
}
// ensemble distribution should be same
// we don't check the detail ensemble, since new bookie will be set
// using recovery tool.
Iterator keyIter = ensembles.keySet().iterator();
Iterator newMetaKeyIter = newMeta.ensembles.keySet().iterator();
for (int i=0; i> newEnsembles) {
// allow new metadata to be one ensemble less than current metadata
// since ensemble change might kick in when recovery changed metadata
int diff = ensembles.size() - newEnsembles.size();
if (0 != diff && 1 != diff) {
return;
}
int i = 0;
for (Entry> entry : newEnsembles.entrySet()) {
++i;
if (ensembles.size() != i) {
// we should use last ensemble from current metadata
// not the new metadata read from zookeeper
long key = entry.getKey();
ArrayList ensemble = entry.getValue();
ensembles.put(key, ensemble);
}
}
}
}
LedgerOpenOp.java 0000664 0000000 0000000 00000015265 12445073612 0034113 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/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.
*
*/
package org.apache.bookkeeper.client;
import java.util.Arrays;
import java.security.GeneralSecurityException;
import org.apache.bookkeeper.client.AsyncCallback.OpenCallback;
import org.apache.bookkeeper.client.AsyncCallback.ReadLastConfirmedCallback;
import org.apache.bookkeeper.client.BookKeeper.DigestType;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.GenericCallback;
import org.apache.bookkeeper.util.OrderedSafeExecutor.OrderedSafeGenericCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Encapsulates the ledger open operation
*
*/
class LedgerOpenOp implements GenericCallback {
static final Logger LOG = LoggerFactory.getLogger(LedgerOpenOp.class);
final BookKeeper bk;
final long ledgerId;
final OpenCallback cb;
final Object ctx;
LedgerHandle lh;
final byte[] passwd;
final DigestType digestType;
boolean doRecovery = true;
boolean administrativeOpen = false;
/**
* Constructor.
*
* @param bk
* @param ledgerId
* @param digestType
* @param passwd
* @param cb
* @param ctx
*/
public LedgerOpenOp(BookKeeper bk, long ledgerId, DigestType digestType, byte[] passwd,
OpenCallback cb, Object ctx) {
this.bk = bk;
this.ledgerId = ledgerId;
this.passwd = passwd;
this.cb = cb;
this.ctx = ctx;
this.digestType = digestType;
}
public LedgerOpenOp(BookKeeper bk, long ledgerId, OpenCallback cb, Object ctx) {
this.bk = bk;
this.ledgerId = ledgerId;
this.cb = cb;
this.ctx = ctx;
this.passwd = bk.getConf().getBookieRecoveryPasswd();
this.digestType = bk.getConf().getBookieRecoveryDigestType();
this.administrativeOpen = true;
}
/**
* Inititates the ledger open operation
*/
public void initiate() {
/**
* Asynchronously read the ledger metadata node.
*/
bk.getLedgerManager().readLedgerMetadata(ledgerId, this);
}
/**
* Inititates the ledger open operation without recovery
*/
public void initiateWithoutRecovery() {
this.doRecovery = false;
initiate();
}
/**
* Implements Open Ledger Callback.
*/
public void operationComplete(int rc, LedgerMetadata metadata) {
if (BKException.Code.OK != rc) {
// open ledger failed.
cb.openComplete(rc, null, this.ctx);
return;
}
final byte[] passwd;
final DigestType digestType;
/* For an administrative open, the default passwords
* are read from the configuration, but if the metadata
* already contains passwords, use these instead. */
if (administrativeOpen && metadata.hasPassword()) {
passwd = metadata.getPassword();
digestType = metadata.getDigestType();
} else {
passwd = this.passwd;
digestType = this.digestType;
if (metadata.hasPassword()) {
if (!Arrays.equals(passwd, metadata.getPassword())) {
LOG.error("Provided passwd does not match that in metadata");
cb.openComplete(BKException.Code.UnauthorizedAccessException, null, this.ctx);
return;
}
if (digestType != metadata.getDigestType()) {
LOG.error("Provided digest does not match that in metadata");
cb.openComplete(BKException.Code.DigestMatchException, null, this.ctx);
return;
}
}
}
// get the ledger metadata back
try {
lh = new ReadOnlyLedgerHandle(bk, ledgerId, metadata, digestType, passwd, !doRecovery);
} catch (GeneralSecurityException e) {
LOG.error("Security exception while opening ledger: " + ledgerId, e);
cb.openComplete(BKException.Code.DigestNotInitializedException, null, this.ctx);
return;
} catch (NumberFormatException e) {
LOG.error("Incorrectly entered parameter throttle: " + bk.getConf().getThrottleValue(), e);
cb.openComplete(BKException.Code.IncorrectParameterException, null, this.ctx);
return;
}
if (metadata.isClosed()) {
// Ledger was closed properly
cb.openComplete(BKException.Code.OK, lh, this.ctx);
return;
}
if (doRecovery) {
lh.recover(new OrderedSafeGenericCallback(bk.mainWorkerPool, ledgerId) {
@Override
public void safeOperationComplete(int rc, Void result) {
if (rc == BKException.Code.OK) {
cb.openComplete(BKException.Code.OK, lh, LedgerOpenOp.this.ctx);
} else if (rc == BKException.Code.UnauthorizedAccessException) {
cb.openComplete(BKException.Code.UnauthorizedAccessException, null, LedgerOpenOp.this.ctx);
} else {
cb.openComplete(BKException.Code.LedgerRecoveryException, null, LedgerOpenOp.this.ctx);
}
}
});
} else {
lh.asyncReadLastConfirmed(new ReadLastConfirmedCallback() {
@Override
public void readLastConfirmedComplete(int rc,
long lastConfirmed, Object ctx) {
if (rc != BKException.Code.OK) {
cb.openComplete(BKException.Code.ReadException, null, LedgerOpenOp.this.ctx);
} else {
lh.lastAddConfirmed = lh.lastAddPushed = lastConfirmed;
cb.openComplete(BKException.Code.OK, lh, LedgerOpenOp.this.ctx);
}
}
}, null);
}
}
}
LedgerRecoveryOp.java 0000664 0000000 0000000 00000015322 12445073612 0035002 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/client 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.util.Enumeration;
import java.util.concurrent.ScheduledExecutorService;
import org.apache.bookkeeper.client.AsyncCallback.AddCallback;
import org.apache.bookkeeper.client.AsyncCallback.CloseCallback;
import org.apache.bookkeeper.client.AsyncCallback.ReadCallback;
import org.apache.bookkeeper.client.DigestManager.RecoveryData;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.GenericCallback;
import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class encapsulated the ledger recovery operation. It first does a read
* with entry-id of -1 (BookieProtocol.LAST_ADD_CONFIRMED) to all bookies. Then
* starting from the last confirmed entry (from hints in the ledger entries),
* it reads forward until it is not able to find a particular entry. It closes
* the ledger at that entry.
*
*/
class LedgerRecoveryOp implements ReadCallback, AddCallback {
static final Logger LOG = LoggerFactory.getLogger(LedgerRecoveryOp.class);
LedgerHandle lh;
int numResponsesPending;
boolean proceedingWithRecovery = false;
long maxAddPushed = LedgerHandle.INVALID_ENTRY_ID;
long maxAddConfirmed = LedgerHandle.INVALID_ENTRY_ID;
long maxLength = 0;
// keep a copy of metadata for recovery.
LedgerMetadata metadataForRecovery;
GenericCallback cb;
class RecoveryReadOp extends PendingReadOp {
RecoveryReadOp(LedgerHandle lh, ScheduledExecutorService scheduler, long startEntryId,
long endEntryId, ReadCallback cb, Object ctx) {
super(lh, scheduler, startEntryId, endEntryId, cb, ctx);
}
@Override
protected LedgerMetadata getLedgerMetadata() {
return metadataForRecovery;
}
}
public LedgerRecoveryOp(LedgerHandle lh, GenericCallback cb) {
this.cb = cb;
this.lh = lh;
numResponsesPending = lh.metadata.getEnsembleSize();
}
public void initiate() {
ReadLastConfirmedOp rlcop = new ReadLastConfirmedOp(lh,
new ReadLastConfirmedOp.LastConfirmedDataCallback() {
public void readLastConfirmedDataComplete(int rc, RecoveryData data) {
if (rc == BKException.Code.OK) {
lh.lastAddPushed = lh.lastAddConfirmed = data.lastAddConfirmed;
lh.length = data.length;
// keep a copy of ledger metadata before proceeding
// ledger recovery
metadataForRecovery = new LedgerMetadata(lh.getLedgerMetadata());
doRecoveryRead();
} else if (rc == BKException.Code.UnauthorizedAccessException) {
cb.operationComplete(rc, null);
} else {
cb.operationComplete(BKException.Code.ReadException, null);
}
}
});
/**
* Enable fencing on this op. When the read request reaches the bookies
* server it will fence off the ledger, stopping any subsequent operation
* from writing to it.
*/
rlcop.initiateWithFencing();
}
/**
* Try to read past the last confirmed.
*/
private void doRecoveryRead() {
long nextEntry = lh.lastAddConfirmed + 1;
try {
new RecoveryReadOp(lh, lh.bk.scheduler, nextEntry, nextEntry, this, null).initiate();
} catch (InterruptedException e) {
readComplete(BKException.Code.InterruptedException, lh, null, null);
}
}
@Override
public void readComplete(int rc, LedgerHandle lh, Enumeration seq, Object ctx) {
if (rc == BKException.Code.OK) {
LedgerEntry entry = seq.nextElement();
byte[] data = entry.getEntry();
/*
* We will add this entry again to make sure it is written to enough
* replicas. We subtract the length of the data itself, since it will
* be added again when processing the call to add it.
*/
synchronized (lh) {
lh.length = entry.getLength() - (long) data.length;
}
lh.asyncRecoveryAddEntry(data, 0, data.length, this, null);
return;
}
if (rc == BKException.Code.NoSuchEntryException || rc == BKException.Code.NoSuchLedgerExistsException) {
lh.asyncCloseInternal(new CloseCallback() {
@Override
public void closeComplete(int rc, LedgerHandle lh, Object ctx) {
if (rc != BKException.Code.OK) {
LOG.warn("Close failed: " + BKException.getMessage(rc));
cb.operationComplete(rc, null);
} else {
cb.operationComplete(BKException.Code.OK, null);
LOG.debug("After closing length is: {}", lh.getLength());
}
}
}, null, BKException.Code.LedgerClosedException);
return;
}
// otherwise, some other error, we can't handle
LOG.error("Failure " + BKException.getMessage(rc) + " while reading entry: " + (lh.lastAddConfirmed + 1)
+ " ledger: " + lh.ledgerId + " while recovering ledger");
cb.operationComplete(rc, null);
return;
}
@Override
public void addComplete(int rc, LedgerHandle lh, long entryId, Object ctx) {
if (rc != BKException.Code.OK) {
// Give up, we can't recover from this error
LOG.error("Failure " + BKException.getMessage(rc) + " while writing entry: " + (lh.lastAddConfirmed + 1)
+ " ledger: " + lh.ledgerId + " while recovering ledger");
cb.operationComplete(rc, null);
return;
}
doRecoveryRead();
}
}
MacDigestManager.java 0000664 0000000 0000000 00000005120 12445073612 0034710 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/client 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.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class MacDigestManager extends DigestManager {
final static Logger LOG = LoggerFactory.getLogger(MacDigestManager.class);
public static String DIGEST_ALGORITHM = "SHA-1";
public static String KEY_ALGORITHM = "HmacSHA1";
final byte[] passwd;
private final ThreadLocal mac = new ThreadLocal() {
@Override
protected Mac initialValue() {
try {
byte[] macKey = genDigest("mac", passwd);
SecretKeySpec keySpec = new SecretKeySpec(macKey, KEY_ALGORITHM);
Mac mac = Mac.getInstance(KEY_ALGORITHM);
mac.init(keySpec);
return mac;
} catch (GeneralSecurityException gse) {
LOG.error("Couldn't not get mac instance", gse);
return null;
}
}
};
public MacDigestManager(long ledgerId, byte[] passwd) throws GeneralSecurityException {
super(ledgerId);
this.passwd = passwd;
}
static byte[] genDigest(String pad, byte[] passwd) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance(DIGEST_ALGORITHM);
digest.update(pad.getBytes());
digest.update(passwd);
return digest.digest();
}
@Override
int getMacCodeLength() {
return 20;
}
@Override
byte[] getValueAndReset() {
return mac.get().doFinal();
}
@Override
void update(byte[] data, int offset, int length) {
mac.get().update(data, offset, length);
}
}
PendingAddOp.java 0000664 0000000 0000000 00000016263 12445073612 0034063 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/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.
*/
package org.apache.bookkeeper.client;
import java.util.HashSet;
import java.util.Set;
import java.net.InetSocketAddress;
import org.apache.bookkeeper.client.AsyncCallback.AddCallback;
import org.apache.bookkeeper.proto.BookieProtocol;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.WriteCallback;
import org.jboss.netty.buffer.ChannelBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This represents a pending add operation. When it has got success from all
* bookies, it sees if its at the head of the pending adds queue, and if yes,
* sends ack back to the application. If a bookie fails, a replacement is made
* and placed at the same position in the ensemble. The pending adds are then
* rereplicated.
*
*
*/
class PendingAddOp implements WriteCallback {
final static Logger LOG = LoggerFactory.getLogger(PendingAddOp.class);
ChannelBuffer toSend;
AddCallback cb;
Object ctx;
long entryId;
int entryLength;
Set writeSet;
DistributionSchedule.AckSet ackSet;
boolean completed = false;
LedgerHandle lh;
boolean isRecoveryAdd = false;
PendingAddOp(LedgerHandle lh, AddCallback cb, Object ctx) {
this.lh = lh;
this.cb = cb;
this.ctx = ctx;
this.entryId = LedgerHandle.INVALID_ENTRY_ID;
ackSet = lh.distributionSchedule.getAckSet();
}
/**
* Enable the recovery add flag for this operation.
* @see LedgerHandle#asyncRecoveryAddEntry
*/
PendingAddOp enableRecoveryAdd() {
isRecoveryAdd = true;
return this;
}
void setEntryId(long entryId) {
this.entryId = entryId;
writeSet = new HashSet(lh.distributionSchedule.getWriteSet(entryId));
}
void sendWriteRequest(int bookieIndex) {
int flags = isRecoveryAdd ? BookieProtocol.FLAG_RECOVERY_ADD : BookieProtocol.FLAG_NONE;
lh.bk.bookieClient.addEntry(lh.metadata.currentEnsemble.get(bookieIndex), lh.ledgerId, lh.ledgerKey, entryId, toSend,
this, bookieIndex, flags);
}
void unsetSuccessAndSendWriteRequest(int bookieIndex) {
if (toSend == null) {
// this addOp hasn't yet had its mac computed. When the mac is
// computed, its write requests will be sent, so no need to send it
// now
return;
}
// Suppose that unset doesn't happen on the write set of an entry. In this
// case we don't need to resend the write request upon an ensemble change.
// We do need to invoke #sendAddSuccessCallbacks() for such entries because
// they may have already completed, but they are just waiting for the ensemble
// to change.
// E.g.
// ensemble (A, B, C, D), entry k is written to (A, B, D). An ensemble change
// happens to replace C with E. Entry k does not complete until C is
// replaced with E successfully. When the ensemble change completes, it tries
// to unset entry k. C however is not in k's write set, so no entry is written
// again, and no one triggers #sendAddSuccessCallbacks. Consequently, k never
// completes.
//
// We call sendAddSuccessCallback when unsetting t cover this case.
if (!writeSet.contains(bookieIndex)) {
lh.sendAddSuccessCallbacks();
return;
}
if (LOG.isDebugEnabled()) {
LOG.debug("Unsetting success for ledger: " + lh.ledgerId + " entry: " + entryId + " bookie index: "
+ bookieIndex);
}
// if we had already heard a success from this array index, need to
// increment our number of responses that are pending, since we are
// going to unset this success
ackSet.removeBookie(bookieIndex);
completed = false;
sendWriteRequest(bookieIndex);
}
void initiate(ChannelBuffer toSend, int entryLength) {
this.toSend = toSend;
this.entryLength = entryLength;
for (int bookieIndex : writeSet) {
sendWriteRequest(bookieIndex);
}
}
@Override
public void writeComplete(int rc, long ledgerId, long entryId, InetSocketAddress addr, Object ctx) {
int bookieIndex = (Integer) ctx;
if (completed) {
// I am already finished, ignore incoming responses.
// otherwise, we might hit the following error handling logic, which might cause bad things.
return;
}
switch (rc) {
case BKException.Code.OK:
// continue
break;
case BKException.Code.LedgerFencedException:
LOG.warn("Fencing exception on write: L{} E{} on {}",
new Object[] { ledgerId, entryId, addr });
lh.handleUnrecoverableErrorDuringAdd(rc);
return;
case BKException.Code.UnauthorizedAccessException:
LOG.warn("Unauthorized access exception on write: L{} E{} on {}",
new Object[] { ledgerId, entryId, addr });
lh.handleUnrecoverableErrorDuringAdd(rc);
return;
default:
LOG.warn("Write did not succeed: L{} E{} on {}",
new Object[] { ledgerId, entryId, addr });
lh.handleBookieFailure(addr, bookieIndex);
return;
}
if (!writeSet.contains(bookieIndex)) {
LOG.warn("Received a response for (lid:{}, eid:{}) from {}@{}, but it doesn't belong to {}.",
new Object[] { ledgerId, entryId, addr, bookieIndex, writeSet });
return;
}
if (ackSet.addBookieAndCheck(bookieIndex) && !completed) {
completed = true;
LOG.debug("Complete (lid:{}, eid:{}).", ledgerId, entryId);
// when completed an entry, try to send success add callbacks in order
lh.sendAddSuccessCallbacks();
}
}
void submitCallback(final int rc) {
if (rc != BKException.Code.OK) {
LOG.error("Write of ledger entry to quorum failed: L{} E{}",
lh.getId(), entryId);
}
cb.addComplete(rc, lh, entryId, ctx);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("PendingAddOp(lid:").append(lh.ledgerId)
.append(", eid:").append(entryId).append(", completed:")
.append(completed).append(")");
return sb.toString();
}
}
PendingReadOp.java 0000664 0000000 0000000 00000034264 12445073612 0034247 0 ustar 00root root 0000000 0000000 bookkeeper-release-4.2.4/bookkeeper-server/src/main/java/org/apache/bookkeeper/client 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.util.ArrayList;
import java.util.BitSet;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.bookkeeper.client.AsyncCallback.ReadCallback;
import org.apache.bookkeeper.client.BKException.BKDigestMatchException;
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks.ReadEntryCallback;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBufferInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 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, ReadEntryCallback {
Logger LOG = LoggerFactory.getLogger(PendingReadOp.class);
final int speculativeReadTimeout;
final private ScheduledExecutorService scheduler;
private ScheduledFuture> speculativeTask = null;
Queue seq;
Set heardFromHosts;
ReadCallback cb;
Object ctx;
LedgerHandle lh;
long numPendingEntries;
long startEntryId;
long endEntryId;
final int maxMissedReadsAllowed;
class LedgerEntryRequest extends LedgerEntry {
final static int NOT_FOUND = -1;
int nextReplicaIndexToReadFrom = 0;
AtomicBoolean complete = new AtomicBoolean(false);
int firstError = BKException.Code.OK;
int numMissedEntryReads = 0;
final ArrayList