suxiaogang223 commented on code in PR #65381:
URL: https://github.com/apache/doris/pull/65381#discussion_r3611910923


##########
fe/be-java-extensions/paimon-connector/src/main/java/org/apache/doris/paimon/PaimonJniWriter.java:
##########
@@ -0,0 +1,370 @@
+// 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.doris.paimon;
+
+import 
org.apache.doris.common.security.authentication.PreExecutionAuthenticator;
+import 
org.apache.doris.common.security.authentication.PreExecutionAuthenticatorCache;
+
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.memory.RootAllocator;
+import org.apache.arrow.vector.VectorSchemaRoot;
+import org.apache.arrow.vector.ipc.ArrowStreamReader;
+import org.apache.paimon.CoreOptions;
+import org.apache.paimon.disk.IOManager;
+import org.apache.paimon.disk.IOManagerImpl;
+import org.apache.paimon.memory.HeapMemorySegmentPool;
+import org.apache.paimon.table.FileStoreTable;
+import org.apache.paimon.table.sink.BatchTableWrite;
+import org.apache.paimon.table.sink.CommitMessage;
+import org.apache.paimon.table.sink.TableWriteImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * JNI entry point for Paimon write operations.
+ *
+ * <p>Called from C++ ({@code JniPaimonWriter}) via JNI. One instance per BE 
pipeline
+ * fragment (one per {@code VPaimonTableWriter}). Data path:
+ *
+ * <pre>
+ *   C++ Block → Arrow IPC Stream → JNI direct ByteBuffer
+ *   → PaimonJniWriter.write(directBuffer)
+ *   → ArrowStreamReader → VectorSchemaRoot
+ *   → PaimonArrowConverter (column-major typed extraction)
+ *   → PaimonWriteSchema.tableRow() (canonical table-schema order)
+ *   → BatchTableWrite.write(row) (SDK-owned routing and buffering)
+ * </pre>
+ *
+ * <p>Commit path:
+ *
+ * <pre>
+ *   VPaimonTableWriter::close() → JNI → PaimonJniWriter.prepareCommit()
+ *   → BatchTableWrite.prepareCommit()
+ *   → PaimonCommitCodec.encode() → DPCM-framed byte[][]
+ *   → C++ collects TPaimonCommitMessage[] → RPC to FE → PaimonTransaction
+ * </pre>
+ */
+public class PaimonJniWriter {
+    private static final Logger LOG = 
LoggerFactory.getLogger(PaimonJniWriter.class);
+
+    private final BufferAllocator allocator;
+    private final ClassLoader classLoader;
+    private final PaimonArrowConverter arrowConverter = new 
PaimonArrowConverter();
+    private final PaimonCommitCodec commitCodec = new PaimonCommitCodec();
+
+    private PreExecutionAuthenticator preExecutionAuthenticator;
+
+    private PaimonWriteSchema writeSchema;
+    private BatchTableWrite writer;
+    private TableWriteImpl<?> tableWrite;
+    private IOManager ioManager;
+    private long commitIdentifier;
+
+    public PaimonJniWriter() {
+        this.allocator = new RootAllocator(Long.MAX_VALUE);

Review Comment:
   Agreed. This PR fixes the native IPC stream allocation by routing it through 
Doris' tracked Arrow pool, but it does not solve Java `RootAllocator`, 
full-batch `Object[][]`, per-row `GenericRow`, and Paimon write-buffer 
accounting. A correct fix needs an explicit query-budget reserve/release 
contract across JNI plus bounded or streaming row conversion; changing only the 
allocator limit would move the failure point without providing coherent Doris 
workload-group accounting. I will keep this thread unresolved and handle it as 
a separate memory-architecture change with focused concurrency/OOM tests.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonTransaction.java:
##########
@@ -0,0 +1,327 @@
+// 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.doris.datasource.paimon;
+
+import org.apache.doris.common.UserException;
+import org.apache.doris.common.security.authentication.ExecutionAuthenticator;
+import org.apache.doris.datasource.mvcc.MvccUtil;
+import 
org.apache.doris.nereids.trees.plans.commands.insert.InsertCommandContext;
+import 
org.apache.doris.nereids.trees.plans.commands.insert.PaimonInsertCommandContext;
+import org.apache.doris.thrift.TPaimonCommitMessage;
+import org.apache.doris.transaction.Transaction;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.paimon.io.DataInputDeserializer;
+import org.apache.paimon.table.InnerTable;
+import org.apache.paimon.table.sink.CommitMessage;
+import org.apache.paimon.table.sink.CommitMessageSerializer;
+import org.apache.paimon.table.sink.InnerTableCommit;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Paimon transaction.
+ *
+ * Lifecycle:
+ *   1. beginInsert() — record target table
+ *   2. updateCommitMessages() — called multiple times as BE reports commit 
data
+ *   3. commit() — deserialize all CommitMessages, call 
StreamTableCommit.filterAndCommit()
+ *   4. rollback() — abort uncommitted data files
+ *
+ * CommitMessage wire format:
+ *   BE ← JNI ← Java: byte[] with DPCM header (magic + version + length) + 
Paimon
+ *   CommitMessageSerializer payload
+ */
+public class PaimonTransaction implements Transaction {
+    private static final Logger LOG = 
LogManager.getLogger(PaimonTransaction.class);
+
+    private static final int COMMIT_HEADER_SIZE = 12;
+    private static final byte[] COMMIT_MAGIC = new byte[] {'D', 'P', 'C', 'M'};
+
+    private final PaimonMetadataOps ops;
+    private final long transactionId;
+    private final String commitUser;
+    private PaimonExternalTable table;
+    private boolean committed = false;
+    private boolean overwrite = false;
+    private Map<String, String> staticPartition = new HashMap<>();
+
+    private final List<byte[]> commitPayloads = Lists.newArrayList();

Review Comment:
   Agreed; this is not addressed in bc98f94c3e8. The existing external 
transaction managers are in-memory, so durable Paimon failover requires 
persisting the target table, overwrite/static-partition context, commit 
payloads, and transaction outcome, followed by leader-side reconciliation and 
cleanup. That recovery contract is broader than this writer PR and should be 
designed consistently for external transactions rather than adding partial 
Paimon-only persistence here. This PR only prevents unsafe abort when the 
commit outcome is unknown. I am leaving the thread unresolved for the follow-up 
recovery work.



##########
be/src/exec/pipeline/pipeline_fragment_context.cpp:
##########
@@ -2515,6 +2524,20 @@ void 
PipelineFragmentContext::_coordinator_callback(const ReportStatusRequest& r
         }
     }
 
+    if (auto pcm = req.runtime_state->paimon_commit_messages(); !pcm.empty()) {
+        params.__isset.paimon_commit_messages = true;
+        
params.paimon_commit_messages.insert(params.paimon_commit_messages.end(), 
pcm.begin(),
+                                             pcm.end());
+    } else if (!req.runtime_states.empty()) {
+        for (auto* rs : req.runtime_states) {
+            if (auto rs_pcm = rs->paimon_commit_messages(); !rs_pcm.empty()) {
+                params.__isset.paimon_commit_messages = true;
+                
params.paimon_commit_messages.insert(params.paimon_commit_messages.end(),

Review Comment:
   Agreed; this is not addressed in bc98f94c3e8. Codec chunking alone cannot 
bound the final Thrift message because `PipelineFragmentContext` aggregates all 
runtime-state payloads into one status report. Safely splitting it requires 
idempotent batch identifiers, FE acknowledgements and accumulation, retry 
semantics, and correct abort behavior for partially delivered metadata; sending 
multiple reports under the current one-shot contract could duplicate or lose 
commit messages. I am leaving this thread unresolved for a protocol-level 
follow-up.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to