-
Notifications
You must be signed in to change notification settings - Fork 14k
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
MINOR: Cache topic resolution in TopicIds set #17285
Changes from 1 commit
165ee41
a59e8c5
03b4cdc
48c022c
655cddd
536dfbb
25b048e
0d8485a
5aaf623
813d79c
99e9f81
0589e93
fe5056e
2059889
3b555b8
2df3ff0
945023d
f0a2d43
f438bb4
b905a9f
d9254d5
0b97fa6
1f9c7ae
e94ed0f
7281eb2
02653fc
f5d98e0
7e718fb
3c71192
43a881f
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 |
---|---|---|
|
@@ -21,25 +21,169 @@ | |
import org.apache.kafka.image.TopicsImage; | ||
|
||
import java.util.Collection; | ||
import java.util.HashMap; | ||
import java.util.Iterator; | ||
import java.util.Map; | ||
import java.util.NoSuchElementException; | ||
import java.util.Objects; | ||
import java.util.Set; | ||
|
||
/** | ||
* TopicIds is initialized with topic names (String) but exposes a Set of topic ids (Uuid) to the | ||
* user and performs the conversion lazily with TopicsImage. | ||
* user and performs the conversion lazily with a TopicResolver backed by a TopicsImage. | ||
*/ | ||
public class TopicIds implements Set<Uuid> { | ||
/** | ||
* Converts between topic ids (Uuids) and topic names (Strings). | ||
*/ | ||
public interface TopicResolver { | ||
/** | ||
* @return The TopicsImage used by the resolver. | ||
*/ | ||
TopicsImage image(); | ||
|
||
/** | ||
* Converts a topic id to a topic name. | ||
* | ||
* @param id The topic id. | ||
* @return The topic name for the given topic id, or null if the topic does not exist. | ||
*/ | ||
String name(Uuid id); | ||
|
||
/** | ||
* Converts a topic name to a topic id. | ||
* | ||
* @param name The topic name. | ||
* @return The topic id for the given topic name, or null if the topic does not exist. | ||
*/ | ||
Uuid id(String name); | ||
|
||
/** | ||
* Clears any cached data. | ||
* | ||
* Used for benchmarking purposes. | ||
*/ | ||
void clear(); | ||
} | ||
|
||
/** | ||
* A base implementation of TopicResolver. | ||
* | ||
* Provides an implementation of equals and hashCode based on the underlying TopicsImage. | ||
*/ | ||
public abstract static class BaseTopicResolver implements TopicResolver { | ||
protected final TopicsImage image; | ||
|
||
public BaseTopicResolver( | ||
TopicsImage image | ||
) { | ||
this.image = Objects.requireNonNull(image); | ||
} | ||
|
||
@Override | ||
public final TopicsImage image() { | ||
return image; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) return true; | ||
if (o == null || !(o instanceof TopicResolver)) return false; | ||
|
||
TopicResolver that = (TopicResolver) o; | ||
|
||
return Objects.equals(image, that.image()); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return image.hashCode(); | ||
} | ||
} | ||
|
||
/** | ||
* A TopicResolver without any caching. | ||
*/ | ||
public static class DefaultTopicResolver extends BaseTopicResolver { | ||
public DefaultTopicResolver( | ||
TopicsImage image | ||
) { | ||
super(image); | ||
} | ||
|
||
@Override | ||
public String name(Uuid id) { | ||
TopicImage topic = image.getTopic(id); | ||
if (topic == null) return null; | ||
return topic.name(); | ||
} | ||
|
||
@Override | ||
public Uuid id(String name) { | ||
TopicImage topic = image.getTopic(name); | ||
if (topic == null) return null; | ||
return topic.id(); | ||
} | ||
|
||
@Override | ||
public void clear() {} | ||
} | ||
|
||
/** | ||
* A TopicResolver that caches results. | ||
*/ | ||
public static class CachedTopicResolver extends BaseTopicResolver { | ||
private final Map<String, Uuid> topicIds = new HashMap<>(); | ||
private final Map<Uuid, String> topicNames = new HashMap<>(); | ||
|
||
public CachedTopicResolver( | ||
TopicsImage image | ||
) { | ||
super(image); | ||
} | ||
|
||
@Override | ||
public String name(Uuid id) { | ||
return topicNames.computeIfAbsent(id, __ -> { | ||
TopicImage topic = image.getTopic(id); | ||
if (topic == null) return null; | ||
return topic.name(); | ||
}); | ||
} | ||
|
||
@Override | ||
public Uuid id(String name) { | ||
return topicIds.computeIfAbsent(name, __ -> { | ||
TopicImage topic = image.getTopic(name); | ||
if (topic == null) return null; | ||
return topic.id(); | ||
}); | ||
} | ||
|
||
@Override | ||
public void clear() { | ||
this.topicNames.clear(); | ||
this.topicIds.clear(); | ||
} | ||
} | ||
|
||
private final Set<String> topicNames; | ||
private final TopicsImage image; | ||
private final TopicResolver resolver; | ||
|
||
public TopicIds( | ||
Set<String> topicNames, | ||
TopicsImage image | ||
) { | ||
this.topicNames = Objects.requireNonNull(topicNames); | ||
this.image = Objects.requireNonNull(image); | ||
this.resolver = new DefaultTopicResolver(image); | ||
} | ||
|
||
public TopicIds( | ||
Set<String> topicNames, | ||
TopicResolver resolver | ||
) { | ||
this.topicNames = Objects.requireNonNull(topicNames); | ||
this.resolver = Objects.requireNonNull(resolver); | ||
} | ||
|
||
@Override | ||
|
@@ -56,24 +200,24 @@ public boolean isEmpty() { | |
public boolean contains(Object o) { | ||
if (o instanceof Uuid) { | ||
Uuid topicId = (Uuid) o; | ||
TopicImage topicImage = image.getTopic(topicId); | ||
if (topicImage == null) return false; | ||
return topicNames.contains(topicImage.name()); | ||
String topicName = resolver.name(topicId); | ||
if (topicName == null) return false; | ||
return topicNames.contains(topicName); | ||
} | ||
return false; | ||
} | ||
|
||
private static class TopicIdIterator implements Iterator<Uuid> { | ||
final Iterator<String> iterator; | ||
final TopicsImage image; | ||
final TopicResolver resolver; | ||
private Uuid next = null; | ||
|
||
private TopicIdIterator( | ||
Iterator<String> iterator, | ||
TopicsImage image | ||
TopicResolver resolver | ||
) { | ||
this.iterator = Objects.requireNonNull(iterator); | ||
this.image = Objects.requireNonNull(image); | ||
this.resolver = Objects.requireNonNull(resolver); | ||
} | ||
|
||
@Override | ||
|
@@ -85,9 +229,9 @@ public boolean hasNext() { | |
return false; | ||
} | ||
String next = iterator.next(); | ||
TopicImage topicImage = image.getTopic(next); | ||
if (topicImage != null) { | ||
result = topicImage.id(); | ||
Uuid topicId = resolver.id(next); | ||
if (topicId != null) { | ||
result = topicId; | ||
} | ||
} while (result == null); | ||
next = result; | ||
|
@@ -105,7 +249,7 @@ public Uuid next() { | |
|
||
@Override | ||
public Iterator<Uuid> iterator() { | ||
return new TopicIdIterator(topicNames.iterator(), image); | ||
return new TopicIdIterator(topicNames.iterator(), resolver); | ||
} | ||
|
||
@Override | ||
|
@@ -164,20 +308,20 @@ public boolean equals(Object o) { | |
TopicIds uuids = (TopicIds) o; | ||
|
||
if (!Objects.equals(topicNames, uuids.topicNames)) return false; | ||
return Objects.equals(image, uuids.image); | ||
return Objects.equals(resolver, uuids.resolver); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
int result = topicNames.hashCode(); | ||
result = 31 * result + image.hashCode(); | ||
result = 31 * result + resolver.hashCode(); | ||
return result; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "TopicIds(topicNames=" + topicNames + | ||
", image=" + image + | ||
", resolver=" + resolver + | ||
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. nit: If we toString it here, should we add toString methods to the resolvers too? 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'll add a toString implementation that returns |
||
')'; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Should we keep it private?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it doesn't need to be public.