[
https://issues.apache.org/jira/browse/HDFS-5191?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13771369#comment-13771369
]
Chris Nauroth commented on HDFS-5191:
-------------------------------------
I reviewed the current API and caught up on all prior discussion here and in
HDFS-4953. [~cmccabe], thank you for incorporating the feedback. I'm going to
focus on the interface here. I'll review the implementation details soon too.
I like to review APIs by writing a little code against them. This is what I
came up with (imports trimmed for brevity):
{code}
class Main extends Configured implements Tool {
private static final int BUFFER_MAX_LENGTH = 4 * 1024 * 1024; // 4 MB
private static final Log LOG = LogFactory.getLog(Main.class);
@Override
public int run(String[] args) {
FileSystem fs = null;
FSDataInputStream is = null;
try {
fs = FileSystem.get(this.getConf());
is = fs.open(new Path(args[0]));
ByteBufferFactory bbf = new SimpleByteBufferFactory();
EnumSet<ReadOption> readOpts = EnumSet.noneOf(ReadOption.class);
for (;;) {
ByteBuffer bb = is.read(bbf, BUFFER_MAX_LENGTH, readOpts);
if (bb == null) break; // EOF
// Use data from the buffer here.
bbf.putBuffer(bb);
}
} catch (IOException e) {
LOG.error("Unexpected IOException", e);
} finally {
IOUtils.cleanup(LOG, is, fs);
}
return 0;
}
public static void main(String[] args) throws Exception {
System.exit(ToolRunner.run(new Main(), args));
}
}
{code}
Can someone check that this code (written by someone looking at the API for the
first time) is correct? If so, then that's a good sign that we're on the right
track. :-) I think this code sample shows that most of the earlier feedback
has been addressed. Specifically:
# It's a single interface for client read code for both the cached and uncached
case. I didn't need to check flags or catch a special exception or downcast to
an HDFS class to handle copying vs. zero-copy.
# There is generally less code for the client to write, because there are fewer
things that the client needs to control. (i.e. no {{setAllowShortReads}})
# I did not need to preallocate a fallback buffer that wouldn't be used in the
mmap'd case.
# I did not need to catch exceptions for main flow control.
# The {{ByteBufferFactory}} interface would allow the client to control
ownership of direct buffers for fallback (and the {{SimpleByteBufferFactory}}
ships a default implementation that does this).
# There is no sharing of mutable state between implicitly connected objects
(streams and cursors).
# It looks close to the kind of code sample [~owen.omalley] wanted to achieve
in the comments of HDFS-4953.
# There had been discussion of returning array of {{ByteBuffer}} vs. returning
a single {{ByteBuffer}} with possibility of short read. My preference is for
the latter. The former would have required me to write another nested loop. I
need to code for short read anyway in case the final read before EOF doesn't
match my desired read length.
I think the last remaining open question is around the need for a client to
check if zero-copy is available and potentially run a different code path. One
way we could achieve that now is by adding a {{RejectingByteBufferFactory}}
that always throws, and clients that want that behavior can use it instead of
{{SimpleByteBufferFactory}}. Does anyone have a concrete use case for this?
Without a concrete use case, it's hard to say if the interface is sufficient.
Here are some suggestions, mostly minor adjustments on top of the current patch:
# Shall we add overloads of {{FSDataInputStream#read}} that don't require the
caller to pass {{ByteBufferFactory}} (assumes caller wants a new
{{SimpleByteBufferFactory}}) and don't require the caller to pass read opts
(assumes none)?
# Can we rename {{ByteBufferFactory}} to {{ByteBufferPool}}? [~vicaya] had
made a similar suggestion. "Factory" implies per-call instance creation and
"pool" communicates that it needs to control the lifecycle of instances.
# Can we move those classes to the .io sub-package? They aren't coupled to
anything in .fs.
# We need to fully document the expected contract of {{ByteBufferPool}} for
implementers. Some factors to consider:
## Thread-safety - Is it required for the implementation to guarantee
thread-safety internally? (I assume thread-safety is only required if the
caller intends to use the same one from multiple threads. Whatever the answer,
let's document it.)
## Null - Is null a legal return value? Is it possible for callers to pass
null arguments? (Ideally, null is forbidden, but whatever the answer, let's
document it.)
## Bounds-checking - What should implementers do if given a negative length?
(Runtime exception?)
# When the {{FSDataInputStream}} is not {{HasEnhancedByteBufferAccess}}, we try
to fallback to a copying read. However, this failed when I tested my sample
code against a local file system, because it ultimately still called
{{FSDataInputStream#read(ByteBuffer)}}, which throws
{{UnsupportedOperationException}} in the base class. We'll need to fix this to
achieve the goal of clients writing a single code path that works for any
{{FileSystem}}.
> revisit zero-copy API in FSDataInputStream to make it more intuitive
> --------------------------------------------------------------------
>
> Key: HDFS-5191
> URL: https://issues.apache.org/jira/browse/HDFS-5191
> Project: Hadoop HDFS
> Issue Type: Sub-task
> Components: hdfs-client, libhdfs
> Affects Versions: HDFS-4949
> Reporter: Colin Patrick McCabe
> Assignee: Colin Patrick McCabe
> Attachments: HDFS-5191-caching.001.patch
>
>
> As per the discussion on HDFS-4953, we should revisit the zero-copy API to
> make it more intuitive for new users.
--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira