-
Notifications
You must be signed in to change notification settings - Fork 147
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
Add initial Spring Boot implementation #1305
Conversation
2a21c7b
to
f59860c
Compare
f59860c
to
a03a78e
Compare
a03a78e
to
79def99
Compare
+ "and configured `spring.temporal.testServer.enabled: true`."); | ||
} | ||
return testWorkflowEnvironment.getWorkflowClient(); | ||
case ClientProperties.TARGET_LOCAL_SERVICE: |
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.
What is the difference between this and me explicitly providing localhost:7233
? Can the const just be that and we don't have a case
for it? Or is it that important to reuse the stub instance?
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.
There is none, but we have WorkflowServerStub.newLocalStubs, so I think it would be right to mirror it here.
The main argument for this is that it's much friendlier for new users to don't even worry about which port Temporal occupies by default.
stubsOptionsBuilder.validateAndBuildWithDefaults()); | ||
} | ||
|
||
WorkflowClientOptions.Builder clientOptionsBuilder = WorkflowClientOptions.newBuilder(); |
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.
I assume configure for other options can come later? E.g. can I "inject" (or whatever spring calls it these days) an interceptor or data converter?
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.
"More configuration and deep integration with Spring DI will be coming in subsequent PRs for the Spring Boot story."
The purpose of this PR is to provide a correct structure that will initialize things in the correct order and with reasonable logic. You can't configure almost anything not absolutely essential here yet.
for (String taskQueue : annotation.taskQueues()) { | ||
log.info( | ||
"Registering auto-discovered workflow class {} on a task queue {}", clazz, taskQueue); | ||
Worker worker = workerFactory.newWorker(taskQueue); |
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.
We should reuse workers based on task queue I believe. If I am reading correct, code as it is now could have a worker configured for task queue A, an annotation-based workflow for task queue A, and another annotation-based workflow for task queue A, and this will create three workers which will cause an issue.
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.
and this will create three workers which will cause an issue.
newWorker
reuses workers. With warning in log though. It never was possible to create several workers on the same task queue on the same WorkerFactory.
This code be changed later. I didn't want to touch SDK code in this PR and right now there is no getWorker on WorkerFactory, while there should be. Will be added is a separate PR.
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.
newWorker reuses workers. With warning in log though.
I think workers should be reused here with no warning. I should be able to create two WorkflowImpl
s in the same package with the same task queue without a warning I think. Of course if you configure two workers w/ the same task queue, it should error I think.
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.
Not possible with the current API. I will be adding a new API for WorkerFactory that will allow doing that.
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.
I am suggesting doing it in this method. Something like (untested, just off the top of my head here in comment) for the first part:
Map<String, Worker> workers =
properties.getWorkers().stream()
.collect(Collectors.toMap(
props -> props.getTaskQueue(),
props -> newWorker(workerFactory, props),
(tq1, tq2) -> { throw InvalidArgumentException("Multiple configured workers with task queue: " + tq1); }));
And for the annotation part:
if (autoDiscoveredWorkflowImplementationClasses != null) {
for (Class<?> clazz : autoDiscoveredWorkflowImplementationClasses) {
WorkflowImpl annotation = clazz.getAnnotation(WorkflowImpl.class);
for (String taskQueue : annotation.taskQueues()) {
log.info(
"Registering auto-discovered workflow class {} on a task queue {}", clazz, taskQueue);
Worker worker = workers.computeIfAbsent(taskQueue, workerFactory::newWorker);
configureWorkflowImplementation(worker, clazz);
}
}
}
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.
I don't see a reason for WorkerFactory
shouldn't expose helper methods that make it easier. Our users should have an easier time implementing code like that themselves if they want it. workers map is already inside WorkerFactory. I will prefer to improve WorkerFactory
interface for better flexibility.
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.
Yeah, if we error on multiple configured workers of the same task queue, that's fine.
Set<Worker> workers = | ||
properties.getWorkers().stream() | ||
.map(workerProperties -> newWorker(workerFactory, workerProperties)) | ||
.collect(Collectors.toSet()); |
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.
May want to do a quick pass through here to make sure there aren't two workers configured for the same task queue
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.
It shouldn't be the responsibility of SpringBoot to ensure this. WorkerFactory should never allow the creation of several workers for the same task queue for quite a bit of reasons.
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.
Per #1305 (comment), I just think that it should be an error to configure two workers for the same task queue (and otherwise reuse silently).
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.
If you are talking about verifying integrity of the user configuration - yeah, it makes sense.
(Class<T>) workflowInterface.getInterfaceClass(), | ||
() -> (T) beanFactory.createBean(clazz)); | ||
hasWorkflowMethod = true; | ||
break; |
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.
Or just return here and remove hasWorkflowMethod
variable and always throw after the loop
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.
- This code is nasty, it will go away into the core module into the classes that do reflection lookup. It just made it work right now, this definitely will be cleaned up.
} | ||
} | ||
} | ||
return implementations; |
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.
I wonder if you want to find name + task queue clashes here or let it fail at worker validation time.
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.
It will fail on registration on the workers. I will try to not bring logic like this into spring boot as long as possible. What can be shared, should be shared. The only reason why it can leak here is if we want to give some super-specific error message that can reference a spring boot config.
import org.springframework.boot.context.properties.ConstructorBinding; | ||
import org.springframework.boot.context.properties.NestedConfigurationProperty; | ||
|
||
@ConfigurationProperties(prefix = "spring.temporal") |
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.
Can you think of a use case to wrap all of these properties as a collection and bring this one level higher? (or is there a layer in Spring that lets that already happen?)
For example, what if I want to auto configure two workers with separate clients? With this setup, can I have two workflows on one namespace and two other workflows on another namespace?
Alternatively we could think about making clients and workers accept namespaces so I could provide namespaces
alongside taskQueues
in my @WorkflowImpl
(or maybe workerName
in the annotation instead, unsure). But meh, we can stick to not supporting same-task-queue-different-namespace.
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.
I thought about it a little bit and it's an interesting question. I didn't find an elegant solution yet and I'm gonna tackle it in a separate PR.
Here is the problem. We have too many moving parts.
We have namespaces. So, ideally, we should allow setting this whole config per-namespace (collection as you said). WorkflowImpl and ActivityImpl will have to have a pair namespace:taskQueue for auto-discovery after that. Things become a bit more complex, but in a manageable manner. This is on me to-do list of next things.
Multiple clients. I think it's not really worth the complexity. I do have a to-do in mind on supporting separate client and worker clients in the next PR. Now we are cheating and just have a worker client, but it's not the correct approach. We should have a separate workerClient
and well, clientClient
:) . I probably should've done this part in this PR to establish a correct structure, but I expect it to be a messy and frustrating task of its own and it will be in a separate PR. To do it well, I need to come up with a way for worker and client Clients to share the majority of the config.
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.
👍 Is it possible to mark this Spring Boot stuff as experimental and subject to incompatible changes in the near term?
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.
Already have it in the readme and the module names have -alpha
postfix. I hope it's loud enough, if you have any other ideas to do it - share.
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.
Works for me.
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.
Approving assuming we can still make backwards incompatible changes to this Spring Boot API for a while in the next sets of PRs
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.
I didn't go into the implementation details but the usage part looks great
What was changed
Add an initial barebone Spring boot module that allows simple wiring of workflow and activities with minimal code.
The main goal of this PR is to provide an MVP framework for further improvements. The current state is not production-ready and postfixed with
-alpha
More configuration and deep integration with Spring DI will be coming in subsequent PRs for the Spring Boot story.
One of the goals of this PR is not touching code in core temporal-sdk modules for simplicity of review. All changes that will require changes to core temporal-sdk modules will be coming in separate easier-to-manage PRs.
This PR provides an MVP for #8 and a supporting ground for improvements roughly outlined in #745.