Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

java.lang.IllegalStateException: Unable to index 268435456 #684

Closed
mikeb01 opened this issue Jun 9, 2020 · 10 comments
Closed

java.lang.IllegalStateException: Unable to index 268435456 #684

mikeb01 opened this issue Jun 9, 2020 · 10 comments

Comments

@mikeb01
Copy link

mikeb01 commented Jun 9, 2020

With the attached appender and tailer after ~268M messages I get the following exception

Exception in thread "main" java.lang.IllegalStateException: Unable to index 268435456
	at net.openhft.chronicle.queue.impl.single.SCQIndexing.setPositionForSequenceNumber(SCQIndexing.java:625)
	at net.openhft.chronicle.queue.impl.single.SingleChronicleQueueStore.setPositionForSequenceNumber(SingleChronicleQueueStore.java:350)
	at net.openhft.chronicle.queue.impl.single.SingleChronicleQueueExcerpts$StoreAppender.writeIndexForPosition(SingleChronicleQueueExcerpts.java:689)
	at net.openhft.chronicle.queue.impl.single.SingleChronicleQueueExcerpts$StoreAppender$StoreAppenderContext.close(SingleChronicleQueueExcerpts.java:818)
	at net.openhft.chronicle.queue.impl.single.SingleChronicleQueueExcerpts$StoreAppender$StoreAppenderContext.close(SingleChronicleQueueExcerpts.java:784)
	at net.openhft.chronicle.queue.impl.single.SingleChronicleQueueExcerpts$StoreAppender.writeBytes(SingleChronicleQueueExcerpts.java:174)
	at bitmex.gateway.trading.engine.chronicle.TimestampAppender.main(TimestampAppender.java:21)

And at that point the queue is corrupt.

import net.openhft.chronicle.queue.ExcerptAppender;
import net.openhft.chronicle.queue.RollCycles;
import net.openhft.chronicle.queue.impl.single.SingleChronicleQueue;
import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder;

public class TimestampAppender {

    public static void main(String[] args) {

        SingleChronicleQueue build = SingleChronicleQueueBuilder
                .binary("src/test/resources/sandbox/times_publication")
                .rollCycle(RollCycles.HOURLY)
                .timeProvider(System::currentTimeMillis)
                .build();

        ExcerptAppender excerptAppender = build.acquireAppender().methodWriterBuilder().recordHistory();

        while (true) {
            excerptAppender.writeBytes(bytesOut -> {
                bytesOut.writeLong(System.nanoTime());
            });
        }
    }
}
import net.openhft.chronicle.queue.ExcerptTailer;
import net.openhft.chronicle.queue.RollCycles;
import net.openhft.chronicle.queue.impl.single.SingleChronicleQueue;
import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder;
import net.openhft.chronicle.wire.DocumentContext;

public class TimestampTailer {

    public static void main(String[] args) throws InterruptedException {
        SingleChronicleQueue build = SingleChronicleQueueBuilder
                .binary("src/test/resources/sandbox/times_publication")
                .rollCycle(RollCycles.HOURLY)
                .timeProvider(System::currentTimeMillis)
                .build();

        ExcerptTailer tailer = build.createTailer();

        long previousTimestamp = 0;
        long counter = 0;

        while (true) {
            try (DocumentContext documentContext = tailer.readingDocument()) {
                if (!documentContext.isPresent()) {
                    Thread.sleep(1);
                    continue;
                }

                long timestamp = documentContext.wire().bytes().readLong();
                if (timestamp < previousTimestamp) {
                    throw new RuntimeException();
                }

                ++counter;
                if (counter % 1000000 == 0) {
                    System.out.println("Seen " + counter + " events");
                }

                previousTimestamp = timestamp;
            }
        }
    }
}

uname -a: Darwin MacBook-Pro 19.5.0 Darwin Kernel Version 19.5.0: Tue May 26 20:41:44 PDT 2020; root:xnu-6153.121.2~2/RELEASE_X86_64 x86_64
JDK:

openjdk version "1.8.0_252"
OpenJDK Runtime Environment (Zulu 8.46.0.19-CA-macosx) (build 1.8.0_252-b14)
OpenJDK 64-Bit Server VM (Zulu 8.46.0.19-CA-macosx) (build 25.252-b14, mixed mode)

Chronicle 5.19.32

@JerryShea
Copy link
Contributor

Hi @mikeb01, HOURLY roll cycle is limited to 256M entries per hour so if you use one of the other RollCycles e.g. LARGE_HOURLY_SPARSE I can confirm that you won't see this problem. The only downside of using e.g. LARGE_HOURLY_SPARSE is that less index entries will be written and thus random access seek performance will be affected. If you are not concerned about random access seek performance then the sparse roll cycles are better for write performance too.

public enum RollCycles implements RollCycle {
    ...
    HOURLY(/*----------*/"yyyyMMdd-HH", 60 * 60 * 1000, 4 << 10, 16), // 256 million entries per hour.
    LARGE_HOURLY(/*----*/"yyyyMMdd-HH'L'", 60 * 60 * 1000, 8 << 10, 64), // 2 billion entries per hour.
    LARGE_HOURLY_SPARSE("yyyyMMdd-HH'LS'", 60 * 60 * 1000, 4 << 10, 1024), // 16 billion entries per hour with sparse indexing
    LARGE_HOURLY_XSPARSE("yyyyMMdd-HH'LX'", 60 * 60 * 1000, 2 << 10, 1 << 20), // 16 billion entries per hour with super-sparse indexing

The error message you saw is not very helpful - we will look into improving that.

@JerryShea JerryShea reopened this Jun 10, 2020
@JerryShea
Copy link
Contributor

@mikeb01 I checked this myself but happy to wait for you to close once you have confirmed

@mikeb01
Copy link
Author

mikeb01 commented Jun 10, 2020

Worth noting that it also corrupts the queue and it can't be used after this failure.

@JerryShea
Copy link
Contributor

JerryShea commented Jun 10, 2020 via email

@dpisklov
Copy link
Contributor

I improved the message but we can't fix the behaviour without too many changes, and it's probably not wise to make those changes to cover the user error - the javadoc (now) clearly says how many messages you can write for each roll cycle.

@mikeb01
Copy link
Author

mikeb01 commented Jun 10, 2020

Is there any API to get the remaining time and number of messages left in the current roll cycle?

@dpisklov
Copy link
Contributor

@mikeb01 the time is in the name of the roll cycle. Queue rolls starting from midnight UTC, so e.g. for *HOURLY roll cycles (there're few) the roll will happen at the hour boundary. As for the number of messages left, it's max number of messages per roll cycle minus current sequence number (RollCycle#toSequenceNumber(long) can help you to convert output of appender#index() or tailer#index() to the sequence number within the current roll cycle)

@RobAustin
Copy link
Member

RobAustin commented Jun 10, 2020 via email

@JerryShea
Copy link
Contributor

@mikeb01 the queue should not be corrupt, reads should still work. You just won't be able to write to it until the roll cycle ticks over. Is this what you saw?

@mikeb01
Copy link
Author

mikeb01 commented Jun 12, 2020

I can confirm that the queue is not corrupt, it was just continuing to through the IllegalStateException until the hourly rollover.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants