mayankshriv commented on code in PR #19378:
URL: https://github.com/apache/pinot/pull/19378#discussion_r3936932773


##########
pinot-plugins/pinot-input-format/pinot-protobuf/src/main/java/org/apache/pinot/plugin/inputformat/protobuf/ProtoBufUtils.java:
##########
@@ -20,59 +20,36 @@
 
 import com.google.protobuf.Descriptors;
 import com.google.protobuf.ProtobufInternalUtils;
-import java.io.File;
-import java.io.FileInputStream;
 import java.io.InputStream;
 import java.net.URI;
-import java.nio.file.Files;
-import java.nio.file.Path;
 import org.apache.pinot.spi.filesystem.PinotFS;
 import org.apache.pinot.spi.filesystem.PinotFSFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class ProtoBufUtils {
-  private static final Logger LOGGER = 
LoggerFactory.getLogger(ProtoBufUtils.class);
   public static final String TMP_DIR_PREFIX = "pinot-protobuf";
   public static final String PB_OUTER_CLASS_SUFFIX = "OuterClass";
 
   private ProtoBufUtils() {
   }
 
-  public static File getFileCopiedToLocal(String filePath)
+  /// Reads the contents of a descriptor file (local or remote) into a byte 
array. The file is read via
+  /// [PinotFS#open] and the stream is closed before returning - no temporary 
files are created.
+  ///
+  /// @param descriptorFilePath URI string pointing to a `.desc` protobuf 
descriptor file
+  /// @return the raw bytes of the descriptor file
+  public static byte[] readDescriptorFileBytes(String descriptorFilePath)
       throws Exception {
-    URI fileURI = URI.create(filePath);
+    URI fileURI = URI.create(descriptorFilePath);
     String scheme = fileURI.getScheme();
     if (scheme == null) {
       scheme = PinotFSFactory.LOCAL_PINOT_FS_SCHEME;
     }
-    if (PinotFSFactory.isSchemeSupported(scheme)) {
-      PinotFS pinotFS = PinotFSFactory.create(scheme);
-      Path localTmpDir = Files.createTempDirectory(TMP_DIR_PREFIX + 
System.currentTimeMillis());
-      File localFile = createLocalFile(fileURI, localTmpDir.toFile());
-      LOGGER.info("Copying protocol buffer jar/descriptor file from source: {} 
to dst: {}", filePath,
-          localFile.getAbsolutePath());
-      pinotFS.copyToLocalFile(fileURI, localFile);
-      return localFile;
-    } else {
-      throw new RuntimeException(String.format("Scheme: %s not supported in 
PinotFSFactory"
-          + " for protocol buffer jar/descriptor file: %s.", scheme, 
filePath));
+    PinotFS pinotFS = PinotFSFactory.create(scheme);
+    try (InputStream in = pinotFS.open(fileURI)) {
+      return in.readAllBytes();

Review Comment:
   Reopening this - I implemented it as suggested, but it turns out to be 
unsafe on one of the two paths, so I need your call before changing it back.
   
   `com.github.os72:protobuf-dynamic:1.0.1`, which `ProtoBufMessageDecoder` 
uses:
   
   ```java
   public static DynamicSchema parseFrom(InputStream schemaDescIn) throws ... {
     try {
       int len;
       byte[] buf = new byte[4096];
       ByteArrayOutputStream baos = new ByteArrayOutputStream();
       while ((len = schemaDescIn.read(buf)) > 0) baos.write(buf, 0, len);
       return parseFrom(baos.toByteArray());
     } finally { schemaDescIn.close(); }
   }
   ```
   
   Two problems:
   
   1. **The loop cannot distinguish a 0-return from EOF**, so it stops early 
and parses a truncated descriptor. Truncation on a `FileDescriptorSet` message 
boundary still parses successfully, yielding a descriptor missing message types 
- after which `getMessageTypes().toArray()[0]` can select the wrong type and we 
silently extract the wrong columns. Before this PR the stream was always a 
`FileInputStream` over a local copy, which never returns 0, so the loop was 
safe by construction. Reading via `PinotFS.open()` removes that guarantee (e.g. 
GCS returns `Channels.newInputStream(...)`, and the JDK's `ChannelInputStream` 
propagates a channel's 0 verbatim).
   
   2. **It does not actually stream** - it accumulates the whole descriptor 
into a `ByteArrayOutputStream` before parsing, so there is no allocation saving 
on this path, and `ByteArrayOutputStream` doubling makes the transient 
footprint worse than `readAllBytes()`.
   
   `readAllBytes()` is not exposed to (1): it delegates to `readNBytes`, whose 
outer `do { ... } while (n >= 0 && remaining > 0)` re-enters on a 0-return 
rather than treating it as EOF. Measured with a stream that returns 0 once and 
then 10000 bytes:
   
   ```
   DynamicSchema loop = 0 bytes      -> silently truncated
   readAllBytes()     = 10000 bytes  -> correct
   ```
   
   `ProtoBufRecordReader` is unaffected: 
`FileDescriptorSet.parseFrom(InputStream)` genuinely streams via 
`CodedInputStream` and rejects a 0-return outright (`#read(byte[]) returned 
invalid result:`). Your suggestion is the right call there, so I would keep it.
   
   There is also a knock-on effect for your other comment about proving the 
consumers close the stream: because `DynamicSchema.parseFrom` closes the stream 
itself, that assertion cannot distinguish "our try-with-resources closed it" 
from "the library closed it". I confirmed by mutation that deleting the 
decoder's try-with-resources leaves the test green. So on the decoder path, 
parsing from the stream and proving the caller closes it are mutually exclusive.
   
   Proposed: `readAllBytes()` + `DynamicSchema.parseFrom(byte[])` for the 
decoder, keep streaming for the record reader, and note the asymmetry in a 
comment.
   
   Alternative if you would rather preserve streaming: wrap the stream in a 
filter that retries on a 0-return. That fixes the truncation but leaves the 
close assertion unprovable, and it is extra machinery to work around a 
third-party loop. Note `protobuf-dynamic` 1.0.1 is the latest release and the 
project is dormant, so there is no upstream fix to wait for.
   
   Which would you prefer?
   



-- 
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