final void acknowledge( ConnectionContext context, final TopicMessageStore destination, final String clientId, final String subscriptionName, final MessageId messageId, final MessageAck ack) throws IOException { if (ack.isInTransaction()) { if (ack.getTransactionId().isXATransaction() || theStore.isConcurrentStoreAndDispatchTransactions() == false) { destination.acknowledge(context, clientId, subscriptionName, messageId, ack); } else { Tx tx = getTx(ack.getTransactionId()); tx.add( new RemoveMessageCommand(context) { public MessageAck getMessageAck() { return ack; } public Future<Object> run(ConnectionContext ctx) throws IOException { destination.acknowledge(ctx, clientId, subscriptionName, messageId, ack); return AbstractMessageStore.FUTURE; } }); } } else { destination.acknowledge(context, clientId, subscriptionName, messageId, ack); } }
Future<Object> asyncAddTopicMessage( ConnectionContext context, final MessageStore destination, final Message message) throws IOException { if (message.getTransactionId() != null) { if (message.getTransactionId().isXATransaction() || theStore.isConcurrentStoreAndDispatchTransactions() == false) { destination.addMessage(context, message); return AbstractMessageStore.FUTURE; } else { Tx tx = getTx(message.getTransactionId()); tx.add( new AddMessageCommand(context) { @Override public Message getMessage() { return message; } @Override public Future<Object> run(ConnectionContext ctx) throws IOException { return destination.asyncAddTopicMessage(ctx, message); } }); return AbstractMessageStore.FUTURE; } } else { return destination.asyncAddTopicMessage(context, message); } }
final void removeAsyncMessage( ConnectionContext context, final MessageStore destination, final MessageAck ack) throws IOException { if (ack.isInTransaction()) { if (ack.getTransactionId().isXATransaction() || theStore.isConcurrentStoreAndDispatchTransactions() == false) { destination.removeAsyncMessage(context, ack); } else { Tx tx = getTx(ack.getTransactionId()); tx.add( new RemoveMessageCommand(context) { @Override public MessageAck getMessageAck() { return ack; } @Override public Future<Object> run(ConnectionContext ctx) throws IOException { destination.removeMessage(ctx, ack); return AbstractMessageStore.FUTURE; } }); } } else { destination.removeAsyncMessage(context, ack); } }
/** 添加消息,为了保证添加顺序,这里不得不加锁 */ @Override public void addMessage( final MessageStore store, final long msgId, final PutCommand putCmd, JournalLocation location) throws IOException { if (location == null) { // 非重放,添加put日志 final AppendMessageCommand appendCmd = AppendMessageCommand.newBuilder() .setMessageId(msgId) .setPutCommand(ByteString.copyFrom(putCmd.encode().array())) .build(); final TxCommand txCommand = TxCommand.newBuilder() .setCmdType(TxCommandType.APPEND_MSG) .setCmdContent(appendCmd.toByteString()) .build(); final Tx tx = this.getInflyTx(putCmd.getTransactionId()); if (tx != null) { location = this.journalStore.write(txCommand, null, tx.location, false); } else { location = this.journalStore.write(txCommand, null, null, false); } } final Tx tx = this.getTx(putCmd.getTransactionId(), location); tx.add(store, msgId, putCmd); }
@Override public synchronized void recover(final TransactionRecoveryListener listener) throws IOException { // 所有本地事务都回滚 Map<Object, Tx> copyMap = null; synchronized (this.inflightTransactions) { copyMap = new HashMap<Object, JournalTransactionStore.Tx>(this.inflightTransactions); // this.inflightTransactions.clear(); } for (final Map.Entry<Object, Tx> entry : copyMap.entrySet()) { this.rollback((TransactionId) entry.getKey()); if (log.isDebugEnabled()) { log.debug("Rollback inflight transaction:" + entry.getKey()); } } // 恢复XA中的prepared事务 this.doingRecover = true; try { Map<TransactionId, Tx> txs = null; synchronized (this.preparedTransactions) { txs = new LinkedHashMap<TransactionId, Tx>(this.preparedTransactions); } for (final Map.Entry<TransactionId, Tx> entry : txs.entrySet()) { final Object txid = entry.getKey(); final Tx tx = entry.getValue(); listener.recover((XATransactionId) txid, tx.getRequests()); } } finally { this.doingRecover = false; } }
public void commit( TransactionId txid, boolean wasPrepared, Runnable preCommit, Runnable postCommit) throws IOException { if (txid != null) { if (!txid.isXATransaction() && theStore.isConcurrentStoreAndDispatchTransactions()) { if (preCommit != null) { preCommit.run(); } Tx tx = inflightTransactions.remove(txid); if (tx != null) { List<Future<Object>> results = tx.commit(); boolean doneSomething = false; for (Future<Object> result : results) { try { result.get(); } catch (InterruptedException e) { theStore.brokerService.handleIOException(new IOException(e.getMessage())); } catch (ExecutionException e) { theStore.brokerService.handleIOException(new IOException(e.getMessage())); } catch (CancellationException e) { } if (!result.isCancelled()) { doneSomething = true; } } if (postCommit != null) { postCommit.run(); } if (doneSomething) { KahaTransactionInfo info = getTransactionInfo(txid); theStore.store(new KahaCommitCommand().setTransactionInfo(info), true, null, null); } } else { // The Tx will be null for failed over clients - lets run their post commits if (postCommit != null) { postCommit.run(); } } } else { KahaTransactionInfo info = getTransactionInfo(txid); theStore.store( new KahaCommitCommand().setTransactionInfo(info), true, preCommit, postCommit); forgetRecoveredAcks(txid); } } else { LOG.error("Null transaction passed on commit"); } }
@Test public void testConstructor() { byte[] rawTx = Utils.hexStringToByteArray( "0100000001bdc0141fe3e5c2223a6d26a95acbf791042d93f9d9b8b38f133bf7adb5c1e293010000006a47304402202214770c0f5a9261190337273219a108132a4bc987c745db8dd6daded34b0dcb0220573de1d973166024b8342d6b6fef2a864a06cceee6aee13a910e5d8df465ed2a01210382b259804ad8d88b96a23222e24dd5a130d39588e78960c9e9b48a5b49943649ffffffff02a0860100000000001976a91479a7bf0bba8359561d4dab457042d7b632d5e64188ac605b0300000000001976a914b036c529faeca8040232cc4bd5918e709e90c4ff88ac00000000"); Tx tx = new Tx(rawTx); byte[] txBytes = tx.bitcoinSerialize(); assertTrue(Arrays.equals(rawTx, txBytes)); byte[] exceptTxHash = Utils.reverseBytes( Utils.hexStringToByteArray( "584985ca8a9ed57987da36ea3d13fe05a7c498f2098ddeb6c8d0f3214067640c")); byte[] txHash = tx.getTxHash(); for (Out out : tx.getOuts()) { String outAddress = out.getOutAddress(); } assertTrue(Arrays.equals(exceptTxHash, txHash)); }
@Test public void testDb() { Tx tx = new Tx(); byte[] txHash = Utils.reverseBytes( Utils.hexStringToByteArray( "f8a8335594d4c883f367e003cb3832015640f24714b48bd21cf6fbe84a617dfe")); tx.setTxHash( Utils.reverseBytes( Utils.hexStringToByteArray( "f8a8335594d4c883f367e003cb3832015640f24714b48bd21cf6fbe84a617dfe"))); tx.setBlockNo(304942); tx.setTxTime((int) new Date().getTime() / 1000); tx.setTxVer(1); In inPut = new In(); inPut.setPrevTxHash( Utils.reverseBytes( Utils.hexStringToByteArray( "d7f4efff7aeaffc1630dd3653e923a233fd463f9dc7dd4f97bb5cbf0cf99e56a"))); inPut.setInSn(0); inPut.setTxHash(txHash); inPut.setInSequence(1); inPut.setInSignature(txHash); tx.addInput(inPut); Out out = new Out(); out.setTxHash(txHash); out.setOutSn(0); out.setOutValue(3400); out.setOutScript( Utils.hexStringToByteArray("76a914abceaddc7d791f749671c17dfa36e9b17a4b055588ac")); out.setOutStatus(Out.OutStatus.spent); out.setOutAddress("test"); tx.addOutput(out); AbstractDb.txProvider.add(tx); Tx testTx = AbstractDb.txProvider.getTxDetailByTxHash(txHash); assertEquals( Utils.bytesToHexString(tx.getTxHash()), Utils.bytesToHexString(testTx.getTxHash())); }
public void intercept(final Invocation inv) { Config config = Tx.getConfigWithTxConfig(inv); if (config == null) config = DbKit.getConfig(); if (actionKeySet.contains(inv.getActionKey())) { DbPro.use(config.getName()) .tx( new IAtom() { public boolean run() throws SQLException { inv.invoke(); return true; } }); } else { inv.invoke(); } }
@Override public void commit(final TransactionId txid, final boolean wasPrepared) throws IOException { final Tx tx; if (wasPrepared) { synchronized (this.preparedTransactions) { tx = this.preparedTransactions.remove(txid); } } else { synchronized (this.inflightTransactions) { tx = this.inflightTransactions.remove(txid); } } if (tx == null) { return; } // Append messages final Map<MessageStore, List<Long>> msgIds = tx.getMsgIds(); final Map<MessageStore, List<PutCommand>> putCommands = tx.getPutCommands(); final Map<String, AddMsgLocation> locations = new LinkedHashMap<String, JournalTransactionStore.AddMsgLocation>(); final int count = msgIds.size(); for (final Map.Entry<MessageStore, List<Long>> entry : msgIds.entrySet()) { final MessageStore msgStore = entry.getKey(); final List<Long> ids = entry.getValue(); final List<PutCommand> cmds = putCommands.get(msgStore); // Append message msgStore.append( ids, cmds, new AppendCallback() { @Override public void appendComplete(final Location location) { // Calculate checksum final int checkSum = CheckSum.crc32(MessageUtils.makeMessageBuffer(ids, cmds).array()); final String description = msgStore.getDescription(); // Store append location synchronized (locations) { locations.put( description, new AddMsgLocation( location.getOffset(), location.getLength(), checkSum, description)); // 处理完成 if (locations.size() == count) { // 将位置信息序列化,并作为tx // command的附加数据存储,这部分数据的长度是固定的,因此可以在replay的时候更改 final ByteBuffer localtionBytes = AddMsgLocationUtils.encodeLocation(locations); TxCommand msg = null; // Log transaction final int attachmentLen = localtionBytes.remaining(); if (txid.isXATransaction()) { final TransactionOperation to = TransactionOperation.newBuilder() // .setType(TransactionType.XA_COMMIT) // .setTransactionId(txid.getTransactionKey()) // .setWasPrepared(wasPrepared) // .setDataLength(attachmentLen) // 设置附加数据长度 .build(); msg = TxCommand.newBuilder() .setCmdType(TxCommandType.TX_OP) .setCmdContent(to.toByteString()) .build(); } else { final TransactionOperation to = TransactionOperation.newBuilder() // .setType(TransactionType.LOCAL_COMMIT) // .setTransactionId(txid.getTransactionKey()) // .setWasPrepared(wasPrepared) // .setDataLength(attachmentLen) // 设置附加数据长度 .build(); msg = TxCommand.newBuilder() .setCmdType(TxCommandType.TX_OP) .setCmdContent(to.toByteString()) .build(); } // 记录commit日志,并附加位置信息 try { JournalTransactionStore.this.journalStore.write( msg, localtionBytes, tx.location, true); } catch (final IOException e) { throw new RuntimeException("Write tx log failed", e); } } } } }); } }