awslabs / amazon-kinesis-producer

Amazon Kinesis Producer Library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unable to trap failures

Gauravshah opened this issue · comments

I am trying to use the jar as a library and I am unable to trap any errors.

I am using the following class

        class KinesisFutureCallback
          include FutureCallback
          def initialize logger
            @logger = logger
          end
          def onFailure(e)
            puts "failure"
            @logger.error "Failed in publishing event to Kinesis"
            @logger.error e
          end

          def onSuccess(r)
            puts "success"
            @logger.info "Published event to kinesis #{r}"
          end
        end
@kinesis_callback = KinesisFutureCallback.new(@logger)
            Futures.addCallback(
                @kinesis_producer.addUserRecord(
                    @kinesis_stream_name,
                    Time.now.to_i.to_s,
                    ByteBuffer.wrap({:foo => :bar}.to_json.to_java_bytes)
                ),
                @kinesis_callback)

I am able to trap success. But even if I try to turn off my internet connection I do not end up in the failure block. The library auto-retries. Also if I send a STOP signal to the application it does not terminate at that point. It keeps retrying.

Environment:

  • JRuby 1.7.12 , 1.9 mode
  • JDK "1.8.0_40-ea"
  • Mac OSX 10.10.3
  • amazon-kinesis-producer-0.9.0.jar
  • protobuf-java-2.6.1.jar
  • guava-18.0.jar
  • commons-lang-2.6.jar
  • commons-compress-1.9.jar
  • commons-io-2.4.jar
  • slf4j-simple-1.7.12.jar
  • slf4j-api-1.7.12.jar

Hi Gaurav,

By STOP signal do you mean sending SIGSTOP to the process? SIGSTOP does not (cause the OS to) terminate processes. Have you tried using SIGTERM or SIGKILL?

I'm looking into the first issue where loss of connectivity does not cause requests to fail.

Hi Kevin,

I meant SIGTERM . So lets say that the buffer is empty, sending SIGTERM terminates application gracefully. But lets say the internet was off and it had some data in the buffer then SIGTERM doesn't terminate the program.

Thanks Gaurav. I'll try to reproduce this. If confirmed we'll fix it in an upcoming release.

Let me know if I can help with some detailed steps or a demo. Thanks Kevin

Hi Gaurav,

The 0.10.0 release is now available and should address these issues. Let me know how it goes when you get a chance to try it out.

sure will let you know in 3 days. Thanks @kevincdeng

I am able trap failures now, Thanks @kevincdeng . It gives me a an instance of UserRecordResult which has a list of Attempt . Is there any way I can the message string that was attempted ? So that we have a fallback if something had failed.

Do you mean the actual payload of the record? The UserRecordResult object does not carry that at the moment. To have access to it you can bind the String that is used as the payload to the callback, either by saving it in instances of an explicit class that implements FutureCallback, or capturing it in a closure. Here's some sample code for the latter:

String partitionKey = ...
String payload =  ...
ByteBuffer data = ByteBuffer.wrap(payload.getBytes("UTF-8"));

ListenableFuture<UserRecordResult> f =
        kinesis.addUserRecord(STREAM_NAME, partitionKey, data);
Futures.addCallback(f, new FutureCallback<UserRecordResult>() {
    @Override
    public void onSuccess(UserRecordResult result) {
        long totalTime = result.getAttempts().stream()
                .mapToLong(a -> a.getDelay() + a.getDuration())
                .sum();
        // Only log with a small probability, otherwise it'll be very spammy
        if (RANDOM.nextDouble() < 1e-5) {
            log.info(String.format(
                    "Succesfully put record, partitionKey=%s, payload=%s, " +
                    "sequenceNumber=%s, shardId=%s, took %d attempts, totalling %s ms",
                    partitionKey, payload, result.getSequenceNumber(),
                    result.getShardId(), result.getAttempts().size(), totalTime));
        }
    }

    @Override
    public void onFailure(Throwable t) {
        ...
    };
});

For Java 7 the captured variables have to be marked final.

Yes, I am referring to the payload of data. Thanks for the code sample. But that would not be possible in production environment since I am using only one object of FutureCallback over all the events, is that not the recommended approach ?

That depends. If your callbacks do not have state then a singleton is definitely fine. On the other hand it's also perfectly reasonable to have state in the callbacks. Since in this case we want to save some context in the callback, we'd want to go with the latter.

If you don't want to write the callback code inline (to capture the context automatically in the closure), you can always create a class that implements FutureCallback<UserRecordResult>. This class can take the context you want to save in its constructor (or factory method) and save it into an instance variable. You'd then create a new instance of the class per user record.

Thanks for the explanation @kevincdeng . Will do that for now. Will still love to have ability to get payload from UserRecordResult some time in future.