-
Notifications
You must be signed in to change notification settings - Fork 67
Producer Cache becomes ineffective when ProducerBuilderCustomizer is configured #593
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
Comments
Thanks for submitting this issue and great find! You provided half of the solution already. One way of solving this is by determining the |
Hello @RungePei,
I could not agree more. Also, the details and imagery are quite helpful and very much appreciated. This also reveals a hole in our cache key tests for this case. We do quite a bit of cache key testing at the caching factory level but not w/ the template + caching factory w/ this interceptor combination. This will be added as part of the solution. BackgroundBefore we talk about interceptors lets first talk about the use of Lambdas as a cache key, in general. A couple of facts:
Without enforcing some Same lambda instance (static)The same Lambda instance will match on @RestController
class ControllerConfig {
@Autowired
private PulsarTemplate<User> template;
@GetMapping("/send1")
String send1() {
var user = new User("u1-" + UUID.randomUUID());
var msgId = template.newMessage(user)
.withTopic("user-topic")
.withProducerCustomizer((pb) -> pb.producerName("p1"))
.send();
return "Send1 %s -> %s".formatted(user, msgId);
}
@GetMapping("/send2")
String send2() {
var user = new User("u2-" + UUID.randomUUID());
var msgId = template.newMessage(user)
.withTopic("user-topic")
.withProducerCustomizer((pb) -> pb.producerName("p2"))
.send();
return "Send2 %s -> %s".formatted(user, msgId);
}
} Each endpoint will create its own producer since it is a different Lambda instance.
If the customizer was shared amongst them such as: @RestController
class ControllerConfig {
@Autowired
private PulsarTemplate<User> template;
private ProducerBuilderCustomizer<User> fooCustomizer = (pb) -> pb.producerName("foo");
@GetMapping("/send1")
String send1() {
var user = new User("u1-" + UUID.randomUUID());
var msgId = template.newMessage(user)
.withTopic("user-topic")
.withProducerCustomizer(fooCustomizer)
.send();
return "Send1 %s -> %s".formatted(user, msgId);
}
@GetMapping("/send2")
String send2() {
var user = new User("u2-" + UUID.randomUUID());
var msgId = template.newMessage(user)
.withTopic("user-topic")
.withProducerCustomizer(fooCustomizer)
.send();
return "Send2 %s -> %s".formatted(user, msgId);
}
} Each endpoint will share the same "foo" producer since its the same Lambda instance.
Same Lambda instance (dynamic)Warning If the lambda takes in a variable argument, then it does not match on If we take the same Lambda from above, but instead use a variable to set the producer name: @RestController
class ControllerConfig {
@Autowired
private PulsarTemplate<User> template;
@GetMapping("/send1/{pName}")
String send1(@PathVariable pName) {
var user = new User("u1-" + UUID.randomUUID());
var msgId = template.newMessage(user)
.withTopic("user-topic")
.withProducerCustomizer((b) -> b.producerName(pName))
.send();
return "Send1 %s -> %s".formatted(user, msgId);
}
} This will NEVER cache. This is a big limitation and in cases like this requires an CLICK HERE TO SEE "Goodbye my beautiful baby Lambdas 😿"class ProducerNameBuilderCustomizer implements ProducerBuilderCustomizer<User> {
private final String name;
public ProducerNameBuilderCustomizer(String name) {
this.name = name;
}
@Override
public void customize(ProducerBuilder<User> producerBuilder) {
producerBuilder.producerName(this.name);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ProducerNameBuilderCustomizer that = (ProducerNameBuilderCustomizer) o;
return name.equals(that.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
} SolutionDocument the limitiationDocument the use of Lambda customizers wrt caching (summarize all of my rambling above into a legible section in the reference guide). Add test case(s)A bit of TDD... Fix interceptors issue
Agreed @jonas-grgt . The producer interceptors are just a symptom of the use of Lambdas. If we created them once rather than on every call the CLICK HERE TO SEE "This did the trick locally 😸 " public PulsarTemplate(PulsarProducerFactory<T> producerFactory, List<ProducerInterceptor> interceptors,
SchemaResolver schemaResolver, TopicResolver topicResolver, boolean observationEnabled) {
this.producerFactory = producerFactory;
this.schemaResolver = schemaResolver;
this.topicResolver = topicResolver;
this.observationEnabled = observationEnabled;
this.interceptors = interceptors;
if (!CollectionUtils.isEmpty(this.interceptors)) {
this.interceptorsCustomizers = this.interceptors.stream().map(this::adaptInterceptorToCustomizer).toList();
}
else {
this.interceptorsCustomizers = null;
}
}
private ProducerBuilderCustomizer<T> adaptInterceptorToCustomizer(ProducerInterceptor i) {
return b -> b.intercept(i);
} and then in the private Producer<T> prepareProducerForSend(@Nullable String topic, @Nullable T message, @Nullable Schema<T> schema,
@Nullable Collection<String> encryptionKeys, @Nullable ProducerBuilderCustomizer<T> producerCustomizer) {
Schema<T> resolvedSchema = schema == null ? this.schemaResolver.resolveSchema(message).orElseThrow() : schema;
List<ProducerBuilderCustomizer<T>> customizers = new ArrayList<>();
if (!CollectionUtils.isEmpty(this.interceptorsCustomizers)) {
customizers.addAll(this.interceptorsCustomizers);
}
if (producerCustomizer != null) {
customizers.add(producerCustomizer);
}
return this.producerFactory.createProducer(resolvedSchema, topic, encryptionKeys, customizers);
} When will this be fixed?We will get the above action items in the 1.0.4 and 1.1.0-M2 releases on March 18 and likely in a SNAPSHOT in the next 1-2 days. What's the workaround until then?I think the only option to prevent a continuous |
Leaving open until the docs are updated. |
This commit adds docs to warn users to be careful when using Lambdas for ProducerBuilderCustomizers. See spring-projects#593
This commit modifies the PulsarTemplate to only adapt the list of producer interceptors into a list of producer customizers once in order to properly take the interceptors into account when caching producers. Fixes spring-projects#593 (cherry picked from commit ecec5bb)
This commit adds docs that guide users when implementing ProducerBuilderCustomizers with Lambdas. Resolves spring-projects#593 (cherry picked from commit 32739c3)
When using PulsarTemplate to send messages, the following code snippet is used to generate a Producer:



producerCache uses producerCacheKey as the key. The hashCode of producerCacheKey is as follows:
The customizers are a list whose contents are a lambda,so the hashCode is uncertain.
As a result, the producer cannot be obtained from the producerCache, which causes the producerCache to expand rapidly.
The problem I encountered was when using ProducerInterceptor. If the ProducerInterceptor is configured, it will be last configured to the customizers
The text was updated successfully, but these errors were encountered: