[AIRFLOW-2697] Drop snakebite in favour of hdfs3 - #3560
Conversation
|
The current changes break the One thing that wasn't clear to me about the Besides this, the subclasses duplicate most of the code used in the base class for checking file existence. A good way to avoid this would be to allow users to supply an arbitrary function (or list of functions) for filtering down potential file paths. This may however break the current interface. |
|
New changes add a Beside this, I added a To be honest, I think I would drop the |
gglanzani
left a comment
There was a problem hiding this comment.
Hi @jrderuiter,
I don't have time to review it all now (I'll pick up the rest next week), but I've added a couple of comments in the hdfs_hook.py.
There was a problem hiding this comment.
Maybe hfds_pars could be augmented like so
if configuration.conf.get("core", "security") == "kerberos":
hdfs_pars["hadoop.security.authentication"] = "kerberos"to make existing code not break?
There was a problem hiding this comment.
Good idea. Should the airflow configuration override whatever is set in hdfs_pars? (I.e., should we keep the value for hadoop.security.authentication if it is already set in pars or should we override it?)
There was a problem hiding this comment.
How is the high availability case handled in the new version?
hdfs3 will read from the configuration files the HA settings (see docs), but if we're getting the configuration from the connection, we need to at least specify how to do so in the extra section.
I was thinking that we could specify a ha key in the extra section, so that extra looks like
{
'ha': {
host = "nameservice1"
conf = {
"dfs.nameservices": "nameservice1",
"dfs.ha.namenodes.nameservice1": "namenode113,namenode188",
"dfs.namenode.rpc-address.nameservice1.namenode113": "hostname_of_server1:8020",
"dfs.namenode.rpc-address.nameservice1.namenode188": "hostname_of_server2:8020",
"dfs.namenode.http-address.nameservice1.namenode188": "hostname_of_server1:50070",
"dfs.namenode.http-address.nameservice1.namenode188": "hostname_of_server2:50070"
}
} # number of braces might be wrong here :)and then in the code we could do
ha = params.extra_dejson.get('ha', {})
if ha:
pars.update(ha.get('conf'))
...
self._conn = hdfs3.HDFileSystem(
host=ha.get('host') or params.host or MyNone,
...
)What do you think?
There was a problem hiding this comment.
The way other services support HA via connections is to have multiple rows in the Connections table with different host names but the same conn_id.
(Sorry, quick note, will try to expand on this later)
There was a problem hiding this comment.
Also known as the poor mans load balancing. This will not really work since there is no fallback, but it will pick a random connection, and then you need to pray that one is up.
There was a problem hiding this comment.
True.
Could we add a new method to Connection base/hook to get all connections with the given ID, and use that here?
There was a problem hiding this comment.
Yes, that would work.
Apart from the HA stuff, I think we should also drop hdfs3 and go for PyArrow: https://arrow.apache.org/docs/python/filesystems.html
|
Very nice work @jrderuiter! Since Snakebite is only Python 2.7 compatible, we need to do this anyway. @feng-tao @vfoucault can you take a look? |
|
I like it! But one reason for snakebit was support for Kerberos. libhdfs also supports it, but I am not aware if it needs special configuration to do so. If that is the case please make sure to add it and to test it. |
|
@bolkedebruin it does, see my comment on the code above. |
|
@jrderuiter can you add high available support? |
|
I'll have a look this week! |
|
@Fokko @gglanzani HA + kerberos support has been added, could you check if this is what you had in mind? @Fokko I still need to refactor the HdfsSensorFolder class. To help in refactoring the sensors I added a few deprecation functions (+ one class). Would it make sense to put these in |
Fokko
left a comment
There was a problem hiding this comment.
One minor comment. Looks good to me. Could you also add a line in: https://github.com/apache/incubator-airflow/blob/master/UPDATING.md
Saying that we've moved from snakebite to hdfs3, and that signatures of the methods are changed and this breaks backward compatibility.
There was a problem hiding this comment.
This is file_pattern, right?
|
@jrderuiter I'm wondering how ticket cache is implemented now vs in hdfs3 (http://hdfs3.readthedocs.io/en/latest/_modules/hdfs3/core.html). Give me some time to look into it :) |
|
This won't get into 1.10 anymore. Maybe if we move this to 2.0 we can get rid of the deprecations right away. What are your thoughts on it @bolkedebruin ? |
gglanzani
left a comment
There was a problem hiding this comment.
HA and Kerberos part LGTM besides a couple of details.
There was a problem hiding this comment.
This part could also maybe go to the "UPDATING" doc.
There was a problem hiding this comment.
Reading through hdfs3 docs, it seems that parameters such as user, ticket_cache, and token might also be useful for accessing a kerberized cluster (see here).
These parameters, however, might need to be dag specific (i.e. a dag impersonates user_a, another user_b). So we might put them in the hook's __init__. What do you think?
There was a problem hiding this comment.
I'm not sure, as I would like to avoid adding too many too specific arguments. We could add a single argument hdfs3_kwargs, which contains kwargs that are passed directly to hdfs3.HDFileSystem.
However, it might be better to keep these arguments in the connection, as this provides a more uniform interface for the hooks down the line.
There was a problem hiding this comment.
So, if we provide them in the connection, it means we need a connection per different ticket_cache/user.
@Fokko Is this something usual in Airflow? Providing this in the dag code is more flexible.
@bolkedebruin How are you handling this at ING?
|
Commit #52a2ff3 changes the HdfsSensor classes for the new HdfsHook. The biggest change is that it rewrites the HdfsSensor + HdfsSensorFolder (from contrib) sensors into a HdfsFileSensor and a HdfsFolderSensor class, which aim to retain the functionality of the previous classes. The main difference between the two sensors is that the former checks for files, whilst the latter checks for directories. In doing so, the HdfsFolderSensor also provides options for requiring directories to be (non-)empty, which is difficult to do if we were to keep a single HdfsSensor class. Note that this pulls the HdfsFolderSensor into Besides this, the sensor classes now also accept a list of filter functions, which can filter a list of file/directory paths before the final check. This allows users to filter for files with a given minimum size, for example. I added a few deprecation utility classes/functions to help keep the re-written classes as backwards compatible as possible. However, if we move this to Airflow 2.0 I would consider removing these shims and simply breaking with the old interface, as adapting DAGs to these new sensors should be fairly straightforward. In that case, I would also argue for removing the |
Codecov Report
@@ Coverage Diff @@
## master #3560 +/- ##
==========================================
+ Coverage 77.16% 77.53% +0.37%
==========================================
Files 206 206
Lines 15769 15837 +68
==========================================
+ Hits 12168 12280 +112
+ Misses 3601 3557 -44
Continue to review full report at Codecov.
|
|
@Fokko Any thoughts on my previous comment? |
Fokko
left a comment
There was a problem hiding this comment.
It is evident that a lot of the API's are changed, and this is expected when you switch the underlying API.
For me it would also be acceptable to accept these breaking changes in Airflow 2.0
There was a problem hiding this comment.
I added a few deprecation utility classes/functions to help keep the re-written classes as backwards compatible as possible.
This is not the case here: def __init__(self, regex, *args, **kwargs): -> def __init__(self, pattern, regex, **kwargs): will definitely break.
There was a problem hiding this comment.
I added a few deprecation utility classes/functions to help keep the re-written classes as backwards compatible as possible.
This is not the case here: __init__(self, hdfs_conn_id='hdfs_default', proxy_user=None, autoconfig=False) -> __init__(self, hdfs_conn_id=None)
There was a problem hiding this comment.
I added a few deprecation utility classes/functions to help keep the re-written classes as backwards compatible as possible.
This is not the case here: __init__(self, filepath, ..) -> __init__(self, pattern .. )
There was a problem hiding this comment.
And this is not consistent with the deprecation args: "filepath": "file_pattern",
|
Thanks @Fokko , I'll fix the above cases. I waited with checking everything because I wanted to hear from you guys if you're going to Airflow 2.0 (in which breaking changes would be ok) or Airflow 1.11 (in which they wouldn't). In the case of a minor change, we could also choose to keep but deprecate the old HdfsSensor class and introduce the new HdfsFileSensor/HdfsFolderSensor classes next to the old class, after which we remove the old class in the next major version. |
|
Can you rebase and squash your commits? The we can (most likely :-) ) merge. |
|
@bolkedebruin My preference would be to get rid of the deprecation warnings and target it for Apache Airflow 2.0 |
|
Of course! But the ball needs to get rolling too |
|
@Fokko As we discussed, I removed the deprecation code so we can merge this in preparation for Airflow 2.0. I want to do some final testing, afterwards we can merge if everything passes. |
Fokko
left a comment
There was a problem hiding this comment.
@jrderuiter Cool, thanks! 👍 Don't forget to rebase onto master. Let me know when you're ready.
There was a problem hiding this comment.
Please move this sensor to a separate file.
There was a problem hiding this comment.
Hi @jrderuiter @Fokko , I think it would be good to explicitly tell users that how ignore_exts should be like in the comment (which will be the documentation later).
For example, both {'.py', '.exe'} and {'py', 'exe'} seem valid, but only {'py', 'exe'} would work here.
There was a problem hiding this comment.
Good point @XD-DENG
We could also trim the prepended . from the extension to make both situations work.
There was a problem hiding this comment.
Then maybe also add a .lower() to make sure both {'py', 'exe'} and {'PY', 'EXE'} work?
|
I would love to have this moving forward! Can we get this rebased, so we can start getting this in 2.0? |
|
Maybe move to PyArrow directly then? https://arrow.apache.org/docs/python/filesystems.html |
|
@jrderuiter Can you let me know if you have time/will to move this to PyArrow? Otherwise I will try to take it up on one of the next Fridays. |
|
This PR is Great, I would love to see this moving forward |
|
Is there any update on that matter? Is Airflow going to use PyArrow/Pydoop/* for Python 3 support? |
|
@kouzant We want to move to PyArrow since hdfs3 is not maintained anymore. |
|
@Fokko Looking into it! |
|
Here to check if you still work on this PR? @jrderuiter |
r-richmond
left a comment
There was a problem hiding this comment.
If dropping snakebite is the goal of this PR shouldn't this be tackled as well? (snakebite in kerberos option in setup.py)
|
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Make sure you have checked all steps below.
JIRA
Description
This PR replaces the Snakebite connection used by HdfsHook with hdfs3 to add Python 3 support for the
HdfsHook. Unfortunately, this also required some changes in dependent classes, such as theHdfsSensor, which directly use the underlying connection object and therefore needed to be updated for the library switch. This kind of issue could be avoided in the future by providing a clean interface which dependent classes rely on.Beside this, the
HDFSHookclass has been renamed toHdfsHookfor a more consistent naming convention. For the same reason, thefilepathparameter has been renamed tofile_path.Tests
My PR adds the following unit tests OR does not need testing for this extremely good reason:
tests/hooks/test_hdfs_hook.py
tests/sensors/test_hdfs_sensor.py
Commits
Documentation
Code Quality
git diff upstream/master -u -- "*.py" | flake8 --diff