-
Notifications
You must be signed in to change notification settings - Fork 25.6k
Introduce retention lease persistence #37375
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
Changes from all commits
626601e
40c9a68
33abce2
7aea544
ed43526
dde8e1b
df9f356
c133104
b73c7e1
4f31b23
444f483
5c226d9
d516cd3
cd6a091
628adf8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,13 @@ | |
|
|
||
| package org.elasticsearch.index.seqno; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.Collection; | ||
| import java.util.Collections; | ||
| import java.util.Locale; | ||
| import java.util.Objects; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| /** | ||
| * A "shard history retention lease" (or "retention lease" for short) is conceptually a marker containing a retaining sequence number such | ||
| * that all operations with sequence number at least that retaining sequence number will be retained during merge operations (which could | ||
|
|
@@ -81,18 +88,118 @@ public String source() { | |
| * @param source the source of the retention lease | ||
| */ | ||
| public RetentionLease(final String id, final long retainingSequenceNumber, final long timestamp, final String source) { | ||
| Objects.requireNonNull(id); | ||
| if (id.isEmpty()) { | ||
| throw new IllegalArgumentException("retention lease ID can not be empty"); | ||
| } | ||
| if (id.contains(":") || id.contains(";") || id.contains(",")) { | ||
dnhatn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // retention lease IDs can not contain these characters because they are used in encoding retention leases | ||
| throw new IllegalArgumentException("retention lease ID can not contain any of [:;,] but was [" + id + "]"); | ||
| } | ||
| if (retainingSequenceNumber < SequenceNumbers.UNASSIGNED_SEQ_NO) { | ||
| throw new IllegalArgumentException("retention lease retaining sequence number [" + retainingSequenceNumber + "] out of range"); | ||
| } | ||
| if (timestamp < 0) { | ||
| throw new IllegalArgumentException("retention lease timestamp [" + timestamp + "] out of range"); | ||
| } | ||
| Objects.requireNonNull(source); | ||
| if (source.isEmpty()) { | ||
| throw new IllegalArgumentException("retention lease source can not be empty"); | ||
| } | ||
| if (source.contains(":") || source.contains(";") || source.contains(",")) { | ||
| // retention lease sources can not contain these characters because they are used in encoding retention leases | ||
| throw new IllegalArgumentException("retention lease source can not contain any of [:;,] but was [" + source + "]"); | ||
| } | ||
| this.id = id; | ||
| this.retainingSequenceNumber = retainingSequenceNumber; | ||
| this.timestamp = timestamp; | ||
| this.source = source; | ||
| } | ||
|
|
||
| /** | ||
| * Encodes a retention lease as a string. This encoding can be decoded by {@link #decodeRetentionLease(String)}. The retention lease is | ||
| * encoded in the format <code>id:{id};retaining_seq_no:{retainingSequenecNumber};timestamp:{timestamp};source:{source}</code>. | ||
| * | ||
| * @param retentionLease the retention lease | ||
| * @return the encoding of the retention lease | ||
| */ | ||
| static String encodeRetentionLease(final RetentionLease retentionLease) { | ||
| Objects.requireNonNull(retentionLease); | ||
| return String.format( | ||
| Locale.ROOT, | ||
| "id:%s;retaining_seq_no:%d;timestamp:%d;source:%s", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we should use JSON, with the upside of being more extensible and will allow us to use our parsing infra (and the validation features it has). WDYT?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not particularly concerned about this needing to be extensible. I can be convinced that JSON is worth it but I am doubting. |
||
| retentionLease.id(), | ||
| retentionLease.retainingSequenceNumber(), | ||
| retentionLease.timestamp(), | ||
| retentionLease.source()); | ||
| } | ||
|
|
||
| /** | ||
| * Encodes a collection of retention leases as a string. This encoding can be decoed by {@link #decodeRetentionLeases(String)}. The | ||
| * encoding is a comma-separated encoding of each retention lease as encoded by {@link #encodeRetentionLease(RetentionLease)}. | ||
| * | ||
| * @param retentionLeases the retention leases | ||
| * @return the encoding of the retention leases | ||
| */ | ||
| public static String encodeRetentionLeases(final Collection<RetentionLease> retentionLeases) { | ||
| Objects.requireNonNull(retentionLeases); | ||
| return retentionLeases.stream().map(RetentionLease::encodeRetentionLease).collect(Collectors.joining(",")); | ||
| } | ||
|
|
||
| /** | ||
| * Decodes a retention lease encoded by {@link #encodeRetentionLease(RetentionLease)}. | ||
| * | ||
| * @param encodedRetentionLease an encoded retention lease | ||
| * @return the decoded retention lease | ||
| */ | ||
| static RetentionLease decodeRetentionLease(final String encodedRetentionLease) { | ||
| Objects.requireNonNull(encodedRetentionLease); | ||
| final String[] fields = encodedRetentionLease.split(";"); | ||
| assert fields.length == 4 : Arrays.toString(fields); | ||
| assert fields[0].matches("id:[^:;,]+") : fields[0]; | ||
| final String id = fields[0].substring("id:".length()); | ||
| assert fields[1].matches("retaining_seq_no:\\d+") : fields[1]; | ||
| final long retainingSequenceNumber = Long.parseLong(fields[1].substring("retaining_seq_no:".length())); | ||
| assert fields[2].matches("timestamp:\\d+") : fields[2]; | ||
| final long timestamp = Long.parseLong(fields[2].substring("timestamp:".length())); | ||
| assert fields[3].matches("source:[^:;,]+") : fields[3]; | ||
| final String source = fields[3].substring("source:".length()); | ||
| return new RetentionLease(id, retainingSequenceNumber, timestamp, source); | ||
| } | ||
|
|
||
| /** | ||
| * Decodes retention leases encoded by {@link #encodeRetentionLeases(Collection)}. | ||
| * | ||
| * @param encodedRetentionLeases an encoded collection of retention leases | ||
| * @return the decoded retention leases | ||
| */ | ||
| public static Collection<RetentionLease> decodeRetentionLeases(final String encodedRetentionLeases) { | ||
| Objects.requireNonNull(encodedRetentionLeases); | ||
| if (encodedRetentionLeases.isEmpty()) { | ||
| return Collections.emptyList(); | ||
| } | ||
| assert Arrays.stream(encodedRetentionLeases.split(",")) | ||
| .allMatch(s -> s.matches("id:[^:;,]+;retaining_seq_no:\\d+;timestamp:\\d+;source:[^:;,]+")) | ||
| : encodedRetentionLeases; | ||
| return Arrays.stream(encodedRetentionLeases.split(",")).map(RetentionLease::decodeRetentionLease).collect(Collectors.toList()); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(final Object o) { | ||
| if (this == o) return true; | ||
| if (o == null || getClass() != o.getClass()) return false; | ||
| final RetentionLease that = (RetentionLease) o; | ||
| return Objects.equals(id, that.id) && | ||
| retainingSequenceNumber == that.retainingSequenceNumber && | ||
| timestamp == that.timestamp && | ||
| Objects.equals(source, that.source); | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(id, retainingSequenceNumber, timestamp, source); | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return "RetentionLease{" + | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.