Skip to content
This repository was archived by the owner on Aug 20, 2025. It is now read-only.

Conversation

@merrimanr
Copy link
Contributor

@merrimanr merrimanr commented Oct 31, 2018

Contributor Comments

This PR adds a Stellar REST function that can be used to enrich messages with data from 3rd party REST services. This function leverages the Apache HttpComponents library for Http requests.

The function call follows this format: REST_GET(rest uri, optional rest settings)

There are a handful of settings including basic authentication credentials, proxy support, and timeouts. These settings are included in the RestConfig class. Any of these settings can be defined in the global config under the stellar.rest.settings property and will override any default values. The global config settings can also be overridden by passing in a config object to the expression. This will allow support for multiple REST services that may have different requirements.

Responses are expected to be in JSON format. Successful requests will return a MAP object in Stellar. Errors will be logged and null will be returned by default. There are ways to override this behavior by configuring a list of acceptable status codes and/or values to be returned on error or empty responses.

Considering this will be used on streaming data, how we handle timeouts is important. This function exposes HttpClient timeout settings but those are not enough to keep unwanted latency from being introduced. A hard timeout is implemented in the function to abort a request if the timeout is exceeded. The idea is to guarantee the total request time will not exceed a configured value.

Changes Included

  • HttpClient capability added to Stellar
  • HttpClient setup added to the various bolts and Stellar REPL
  • Utility added for setting up a pooling HttpClient (and possibly other types of clients in the future)
  • Configuration mechanism added for Stellar REST function including settings for authentication, proxy support, timeouts and other settings
  • Function implementation and appropriate unit/integration tests (both unit tests and integration tests are included)

Testing

There are several different ways to test this feature and I would encourage reviewers to get creative and look for cases I may not have thought of. For my testing, I used an online Http service that provides simple endpoints for simulating different use cases: http://httpbin.org/#/. Feel free to try your own or use this one.

I tested this in full dev using the Stellar REPL and the parser and enrichment topologies. First you need to perform a couple setup steps:

  1. Spin up full dev and ensure everything comes up and data is being indexed
  2. Ssh to full dev and install the Squid proxy server:
yum -y install squid
  1. Create a password file that Squid can use for basic authentication
yum -y install httpd-tools
touch /etc/squid/passwd && chown squid /etc/squid/passwd
htpasswd /etc/squid/passwd user # (Will prompt for a password)
  1. Configure Squid for basic authentication by adding these lines to /etc/squid/squid.conf, under the lines with acl Safe_ports*:
auth_param basic program /usr/lib64/squid/ncsa_auth /etc/squid/passwd
auth_param basic children 5
auth_param basic realm Squid Basic Authentication
auth_param basic credentialsttl 2 hours
acl auth_users proxy_auth REQUIRED
http_access allow auth_users
  1. Start Squid and verify it is working correctly:
service squid restart
curl --proxy-user user:password -x node1:3128 http://www.google.com/
  1. Next create password files in HDFS:
su hdfs
cd ~
echo -n 'passwd' > basicPassword.txt
hdfs dfs -put basicPassword.txt /apps/metron
echo -n 'password' > proxyPassword.txt
hdfs dfs -put proxyPassword.txt /apps/metron
exit

To test with the Stellar REPL, follow these steps:

  1. Start the Stellar REPL and verify the REST_GET function is available:
/usr/metron/0.6.1/bin/stellar --zookeeper node1:2181
[Stellar]>>> %functions REST
REST_GET
  1. Test a simple get request:
[Stellar]>>> REST_GET('http://httpbin.org/get')
{args={}, headers={Accept=application/json, Accept-Encoding=gzip,deflate, Cache-Control=max-age=259200, Connection=close, Host=httpbin.org, User-Agent=Apache-HttpClient/4.3.2 (java 1.5)}, origin=127.0.0.1, 136.62.241.236, url=http://httpbin.org/get}
  1. Test a get request with basic authentication:
[Stellar]>>> config := {'basic.auth.user':'user','basic.auth.password.path':'/apps/metron/basicPassword.txt'}
{basic.auth.user=user, basic.auth.password.path=/apps/metron/basicPassword.txt}
[Stellar]>>> REST_GET('http://httpbin.org/basic-auth/user/passwd', config)
{authenticated=true, user=user}
  1. Try the same request without passing in the config. You should get an authentication error:
[Stellar]>>> REST_GET('http://httpbin.org/basic-auth/user/passwd')
2018-10-28 00:32:20 ERROR RestFunctions:161 - Stellar REST request to http://httpbin.org/basic-auth/user/passwd expected status code to be one of [200] but failed with http status code 401: 
java.io.IOException: Stellar REST request to http://httpbin.org/basic-auth/user/passwd expected status code to be one of [200] but failed with http status code 401: 
	at org.apache.metron.stellar.dsl.functions.RestFunctions$RestGet.doGet(RestFunctions.java:209)
	at org.apache.metron.stellar.dsl.functions.RestFunctions$RestGet.apply(RestFunctions.java:157)
	at org.apache.metron.stellar.common.StellarCompiler.lambda$exitTransformationFunc$13(StellarCompiler.java:652)
	at org.apache.metron.stellar.common.StellarCompiler$Expression.apply(StellarCompiler.java:250)
	at org.apache.metron.stellar.common.BaseStellarProcessor.parse(BaseStellarProcessor.java:151)
	at org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor.executeStellar(DefaultStellarShellExecutor.java:409)
	at org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor.execute(DefaultStellarShellExecutor.java:260)
	at org.apache.metron.stellar.common.shell.cli.StellarShell.execute(StellarShell.java:357)
	at org.jboss.aesh.console.AeshProcess.run(AeshProcess.java:53)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
  1. You should also be able to set the basic authentication settings through the global config:
[Stellar]>>> %define stellar.rest.settings := {'basic.auth.user':'user','basic.auth.password.path':'/apps/metron/basicPassword.txt'}
{basic.auth.user=user, basic.auth.password.path=/apps/metron/basicPassword.txt}
[Stellar]>>> REST_GET('http://httpbin.org/basic-auth/user/passwd')
{authenticated=true, user=user}
  1. Now verify you can send a request through the proxy:
[Stellar]>>> config := {'proxy.host':'node1','proxy.port':3128,'proxy.basic.auth.user':'user','proxy.basic.auth.password.path':'/apps/metron/proxyPassword.txt'}
{proxy.basic.auth.password.path=/apps/metron/proxyPassword.txt, proxy.port=3128, proxy.host=node1, proxy.basic.auth.user=user}
[Stellar]>>> REST_GET('http://httpbin.org/get', config)
{args={}, headers={Accept=application/json, Accept-Encoding=gzip,deflate, Cache-Control=max-age=259200, Connection=close, Host=httpbin.org, User-Agent=Apache-HttpClient/4.3.2 (java 1.5)}, origin=127.0.0.1, 136.62.241.236, url=http://httpbin.org/get}
  1. Leave out the proxy credentials, you should get a proxy error:
[Stellar]>>> config := {'proxy.host':'node1','proxy.port':3128}
{proxy.port=3128, proxy.host=node1}
[Stellar]>>> REST_GET('http://httpbin.org/get', config)
2018-10-28 00:43:48 ERROR RestFunctions:161 - Stellar REST request to http://httpbin.org/get expected status code to be one of [200] but failed with http status code 407: <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>ERROR: Cache Access Denied</title>
  1. Timeout is 1000 milliseconds by default. Test the timeout by setting it to 1 or a value where a request won't finish in time. You should get an error:
[Stellar]>>> config := {'timeout': 1} 
{timeout=1}
[Stellar]>>> REST_GET('http://httpbin.org/get', config)
2018-10-28 00:53:07 ERROR RestFunctions:161 - Total Stellar REST request time to http://httpbin.org/get exceeded the configured timeout of 1 ms.
java.io.IOException: Total Stellar REST request time to http://httpbin.org/get exceeded the configured timeout of 1 ms.
	at org.apache.metron.stellar.dsl.functions.RestFunctions$RestGet.doGet(RestFunctions.java:188)
	at org.apache.metron.stellar.dsl.functions.RestFunctions$RestGet.apply(RestFunctions.java:157)
	at org.apache.metron.stellar.common.StellarCompiler.lambda$exitTransformationFunc$13(StellarCompiler.java:652)
	at org.apache.metron.stellar.common.StellarCompiler$Expression.apply(StellarCompiler.java:250)
	at org.apache.metron.stellar.common.BaseStellarProcessor.parse(BaseStellarProcessor.java:151)
	at org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor.executeStellar(DefaultStellarShellExecutor.java:409)
	at org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor.execute(DefaultStellarShellExecutor.java:260)
	at org.apache.metron.stellar.common.shell.cli.StellarShell.execute(StellarShell.java:357)
	at org.jboss.aesh.console.AeshProcess.run(AeshProcess.java:53)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
  1. You can also configure which status codes should be handled as errors. A 404 is considered an error by default:
[Stellar]>>> REST_GET('http://httpbin.org/status/404', config)
2018-10-28 00:56:08 ERROR RestFunctions:161 - Stellar REST request to http://httpbin.org/status/404 expected status code to be one of [200] but failed with http status code 404: 
java.io.IOException: Stellar REST request to http://httpbin.org/status/404 expected status code to be one of [200] but failed with http status code 404: 
	at org.apache.metron.stellar.dsl.functions.RestFunctions$RestGet.doGet(RestFunctions.java:209)
	at org.apache.metron.stellar.dsl.functions.RestFunctions$RestGet.apply(RestFunctions.java:157)
	at org.apache.metron.stellar.common.StellarCompiler.lambda$exitTransformationFunc$13(StellarCompiler.java:652)
	at org.apache.metron.stellar.common.StellarCompiler$Expression.apply(StellarCompiler.java:250)
	at org.apache.metron.stellar.common.BaseStellarProcessor.parse(BaseStellarProcessor.java:151)
	at org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor.executeStellar(DefaultStellarShellExecutor.java:409)
	at org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor.execute(DefaultStellarShellExecutor.java:260)
	at org.apache.metron.stellar.common.shell.cli.StellarShell.execute(StellarShell.java:357)
	at org.jboss.aesh.console.AeshProcess.run(AeshProcess.java:53)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

This behavior can be changed by configuring a 404 to be an acceptable status code and returning an empty object instead of null:

[Stellar]>>> config := {'response.codes.allowed': [200, 404], 'empty.content.override': {}}
{response.codes.allowed=[200, 404], empty.content.override={}}
[Stellar]>>> REST_GET('http://httpbin.org/status/404', config)
{}
  1. The value returned on an error can also be changed from null:
[Stellar]>>> config := {'error.value.override': 'got an error'}
{error.value.override=got an error}
[Stellar]>>> result := REST_GET('http://httpbin.org/status/500', config)
2018-10-28 00:59:41 ERROR RestFunctions:161 - Stellar REST request to http://httpbin.org/status/500 expected status code to be one of [200] but failed with http status code 500: 
java.io.IOException: Stellar REST request to http://httpbin.org/status/500 expected status code to be one of [200] but failed with http status code 500: 
	at org.apache.metron.stellar.dsl.functions.RestFunctions$RestGet.doGet(RestFunctions.java:209)
	at org.apache.metron.stellar.dsl.functions.RestFunctions$RestGet.apply(RestFunctions.java:157)
	at org.apache.metron.stellar.common.StellarCompiler.lambda$exitTransformationFunc$13(StellarCompiler.java:652)
	at org.apache.metron.stellar.common.StellarCompiler$Expression.apply(StellarCompiler.java:250)
	at org.apache.metron.stellar.common.BaseStellarProcessor.parse(BaseStellarProcessor.java:151)
	at org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor.executeStellar(DefaultStellarShellExecutor.java:409)
	at org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor.execute(DefaultStellarShellExecutor.java:260)
	at org.apache.metron.stellar.common.shell.specials.AssignmentCommand.execute(AssignmentCommand.java:66)
	at org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor.execute(DefaultStellarShellExecutor.java:255)
	at org.apache.metron.stellar.common.shell.cli.StellarShell.execute(StellarShell.java:357)
	at org.jboss.aesh.console.AeshProcess.run(AeshProcess.java:53)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
got an error
  1. The REPL should call the close function on exit. To verify this, create a log4j.properties file with the log level set to INFO:
[root@node1 ~]# cat log4j.properties 
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd'T'HH:mm:ss.SSS} %-5p [%c] - %m%n

Start the REPL with the custom log4j.properties file:

/usr/metron/0.6.1/bin/stellar --zookeeper node1:2181 -l log4j.properties

Then run a REST_GET command and quit. The shell should quit successfully and you should see a log message indicating close() was called on the Stellar functions:

[Stellar]>>> REST_GET('http://httpbin.org/get')
{args={}, headers={Accept=application/json, Accept-Encoding=gzip,deflate, Connection=close, Host=httpbin.org, User-Agent=Apache-HttpClient/4.3.2 (java 1.5)}, origin=136.49.61.168, url=http://httpbin.org/get}
[Stellar]>>> quit
2018-11-07T20:34:03.604 INFO  [org.apache.metron.stellar.dsl.functions.resolver.BaseFunctionResolver] - Calling close() on Stellar functions.

To test with the parser and enrichment topologies follow these steps:

  1. Make sure the topologies are running and data is flowing through. It's easier to test if you restart the parser topology with only a single sensor running.
  2. Add a Stellar field transformation to the parser that is still running:
"fieldTransformations": [
    {
      "input": [],
      "output": [
        "parser_rest_result"
      ],
      "transformation": "STELLAR",
      "config": {
        "parser_rest_result": "REST_GET('http://httpbin.org/get?type=parser')"
      }
    }
  ],
  1. Listen on the enrichments Kafka topic. The parser_rest_result field should now be present.
  2. Add a Stellar enrichment to the sensor:
"fieldMap": {
      "geo": [
        "ip_dst_addr",
        "ip_src_addr"
      ],
      "host": [
        "host"
      ],
      "stellar": {
        "config": {
          "enrichment_rest_result": "REST_GET('http://httpbin.org/get?type=enrichment')"
        }
      }
    }
  1. Listen on the indexing Kafka topic. The enrichment_rest_result field should now be present.

Outstanding Issues

  • Caching was briefly discussed in the discuss thread for this feature. Stellar provides a caching mechanism but we may need to be more selective about what is cached right now. I believe this should be a follow on.
  • There was a comment in the Jira related to adding a circuit breaker. Does that need to be done in this PR or can it be a follow on? Should we also explore/discuss a retry strategy?
  • It was also suggested that we create an abstraction for higher latency enrichments such as this in the discuss thread. I would prefer we create a few of these higher latency functions first so that we have a better understanding of how this abstraction would look. Do we want to take that on here?

Pull Request Checklist

Thank you for submitting a contribution to Apache Metron.
Please refer to our Development Guidelines for the complete guide to follow for contributions.
Please refer also to our Build Verification Guidelines for complete smoke testing guides.

In order to streamline the review of the contribution we ask you follow these guidelines and ask you to double check the following:

For all changes:

  • Is there a JIRA ticket associated with this PR? If not one needs to be created at Metron Jira.
  • Does your PR title start with METRON-XXXX where XXXX is the JIRA number you are trying to resolve? Pay particular attention to the hyphen "-" character.
  • Has your PR been rebased against the latest commit within the target branch (typically master)?

For code changes:

  • Have you included steps to reproduce the behavior or problem that is being changed or addressed?

  • Have you included steps or a guide to how the change may be verified and tested manually?

  • Have you ensured that the full suite of tests and checks have been executed in the root metron folder via:

    mvn -q clean integration-test install && dev-utilities/build-utils/verify_licenses.sh 
    
  • Have you written or updated unit tests and or integration tests to verify your changes?

  • If adding new dependencies to the code, are these dependencies licensed in a way that is compatible for inclusion under ASF 2.0?

  • Have you verified the basic functionality of the build by building and running locally with Vagrant full-dev environment or the equivalent?

For documentation related changes:

  • Have you ensured that format looks appropriate for the output in which it is rendered by building and verifying the site-book? If not then run the following commands and the verify changes via site-book/target/site/index.html:

    cd site-book
    mvn site
    

Note:

Please ensure that once the PR is submitted, you check travis-ci for build issues and submit an update to your PR as soon as possible.
It is also recommended that travis-ci is set up for your personal repository such that your branches are built there before submitting a pull request.

* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.metron.stellar.dsl.functions;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do other Stellar functions handle config? This is the only config class I see in stellar-common and I'm wondering if there's another idiom. That aside, there are some existing config examples out there, e.g. PcapConfig and PcapOptions. Any specific reason you're using this approach rather than an enum for your options?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked at 2 different patterns that already exist.

The first was the pattern you mentioned, the ConfigOptions interface. The problem is that this class lives in metron-common which depends on stellar-common. Regardless I don't think pattern is a great fit because it doesn't serialize/deserialize easily and contains a lot of features that are not needed here (functional interfaces for example).

The second was the PROFILER_INIT function in the ProfilerFunctions class. This approach uses a java bean (ProfilerConfig) to hold configuration which allows easy serialization/deserialization. This is actually what I used at first until I made the change to accept a config as an argument expression.

I actually went with option 2 at first but found it cumbersome to merge configs together. I ended up with the pattern I did because a map supports applying properties on top of each other well and serialization/deserialization works without issue. We ended having to do this for PcapConfig and PcapOptions in the REST layer by creating the PcapRequest class which extends a Map.


@Override
public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
super.prepare(stormConf, context, collector);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we necessarily want this in all cases? What about users that never need or want this function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can add a property to configure this. Should it be included by default or not ?

Do you think instantiating this object but never using it would be harmful? It might make things simpler for users if there is one less setting to worry about.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More specifically, shouldn't this be isolated to the stellar function itself? This seems like a bleeding of concerns. With zookeeper, it makes sense to me because we're taking the architectural position that enabling dynamic, real-time configuration loading is part of the framework. I don't think that same general applicability applies to HttpClients. What about using the initialize method instead?

public interface StellarFunction {
  Object apply(List<Object> args, Context context) throws ParseException;
  void initialize(Context context);
  boolean isInitialized();
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried that initially but realized there is no shutdown hook in Stellar. HttpClient objects need to be closed and the only hook that exists is in the bolts.

If we did have a way to close it, I would be in favor of managing it in the function. If we decide it's worth adding that hook in Stellar (I think we should as a follow on) we can easily move this inside the function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm inclined to add the shutdown hook. This stuck out to me also as bleeding concerns a bit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a shutdown hook in Stellar is not trivial and out of scope for this PR in my opinion. I would prefer that be a follow on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I am wrong here? Would adding a shutdown hook be simple?

}

@Override
public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the comment for enrichment - do we want this tied to the bolts this way?

*/
private Object doGet(RestConfig restConfig, HttpGet httpGet, HttpClientContext httpClientContext) throws IOException {

// Schedule a command to abort the httpGet request if the timeout is exceeded
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate in the docs somewhere that this is the hard timeout, and the reason we need the hard timeouts is that the lib provided timeouts are insufficient to achieve the hard timeout?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you see the REST section in the Stellar README? I can add more content to make that clearer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may have forgotten to refresh since before the readme was added. I'll check it out.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that README, could you alter
timeout - Hard timeout for the total request time. Defaults to 1000 ms. to be a bit more explicit about it. E.g. something like
timeout - Stellar enforced hard timeout for the total request time. Defaults to 1000 ms. Client timeouts alone are insufficient to guarantee the hard timeout

Is this a reasonable statement of the problem? Unless I'm mistaken, this gets alluded to a bit, but is never explicitly stated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it. Done.


// Add the basic auth credentials if the rest config settings are present
if (restConfig.getBasicAuthUser() != null && restConfig.getBasicAuthPasswordPath() != null) {
String password = new String(readBytes(new Path(restConfig.getBasicAuthPasswordPath())), StandardCharsets.UTF_8).trim();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we calling trim()? Is whitespace not valid at the front/back of passwords?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just being defensive. I found I had to do this when reading the contents from HDFS. I can look into it again and try to track down exactly what the problem was.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue was with my testing instructions. I was unintentionally adding an extra newline at the end of the password file. I removed trim() and added a warning to the README. Will also update the test instructions.

@merrimanr merrimanr closed this Nov 2, 2018
@merrimanr merrimanr reopened this Nov 2, 2018
@mmiklavc
Copy link
Contributor

mmiklavc commented Nov 2, 2018

@merrimanr - about the need for a close/shutdown hook - why is it needed in the first place? I don't believe we shutdown Zookeeper connections explicitly. They end when the topology spins down. Why should this be any different?

@merrimanr
Copy link
Contributor Author

@mmiklavc
Copy link
Contributor

mmiklavc commented Nov 2, 2018

I'm just not sure we need to shut them down. It's not like there's writing or state-related concerns that push the need to finish with a formal shutdown. The only time we would ever shutdown a client connection would be when we're killing the topology, right?

I really think we need to get this client code out of the bolts. I looked a bit through the Stellar code and if we really think we need this, I think it can be accomplished through a change to the FunctionResolver classes. I'm going to noodle on this a bit.

@merrimanr
Copy link
Contributor Author

I think we should close it, docs are pretty clear about that. I think it's possible connections could be left open on the server side.

Moving the code out of the bolt is an organization/style issue. I think there should be a functional justification like there is for closing a client.

@mmiklavc
Copy link
Contributor

mmiklavc commented Nov 2, 2018

It's not just a style issue. It's an architectural issue, otherwise I wouldn't care.

@mmiklavc
Copy link
Contributor

mmiklavc commented Nov 2, 2018

Throwing this out there for consideration - #1251

@merrimanr
Copy link
Contributor Author

The latest commit incorporates the changes from #1251 and moves the httpclient inside the function. I was able to verify proper closing in the REPL and the EnrichmentIntegrationTest. I was not able to verify in the Storm topologies because Storm does not guarantee these methods will be called in production. I added code comments in the bolts where we are calling StellarFunctions.close().

I spun this up in full dev again and ran through the test plan. Everything continued to work as expected.

@mmiklavc
Copy link
Contributor

mmiklavc commented Nov 8, 2018

Travis failure does not appear related here

@merrimanr merrimanr closed this Nov 8, 2018
@merrimanr merrimanr reopened this Nov 8, 2018
@mmiklavc
Copy link
Contributor

mmiklavc commented Nov 8, 2018

@merrimanr Pending a successful Travis run, I'm +1 by inspection. Thanks!

@justinleet
Copy link
Contributor

+1, thanks!

@asfgit asfgit closed this in 3e73391 Nov 8, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants