Change instance state to OUT_OF_SERVICE with spring cloud netflix (Eureka) - spring-cloud

A spring cloud application is documented to have the following endpoints:
/pause and /resume for calling the Lifecycle methods (stop() and start() on the ApplicationContext)
I suspected that this would be the way in which an instance was marked as OUT_OF_SERVICE in eureka, given the code in EurekaDiscoveryClientConfiguration, a spring class which implements SmartLifecycle:
#Override
public void stop() {
log.info("Unregistering application " + this.instanceConfig.getAppname()
+ " with eureka with status OUT_OF_SERVICE");
if (ApplicationInfoManager.getInstance().getInfo() != null) {
ApplicationInfoManager.getInstance().setInstanceStatus(
InstanceStatus.OUT_OF_SERVICE);
}
this.running.set(false);
}
However, when I POST to the /pause endpoint, I get a 200 response code back (with the value 'true'), but the above code is never executed.
Perhaps I'm not understanding something. If so, how can I trigger the above code to take an instance out of service?
(The close() method of the EurekaDiscoveryClientConfiguration class is called when I shut down the instance - which results in the instance being unregistered - but I'm looking to temporarily suspend service to this instance)

Related

WebClient hanging when using share() followed by block() call but calling block() only returns with an error

I am using the Spring WebFlux WebClient in a spring batch application and I am getting an error when I call block. The code is really simple but I am getting an error when the application I try launching a job from a Rest endpoint on a controller in the batch job.
The rest endpoint is like this:
#RequestMapping("/migration/products/catalog
class ProductController{
private final Job job;
ResponseEntity<Map<String,Object> loadProductCatalog(){
// Code to launch Product Catalog Job
}
}
Here is the method the calls a remote client to get Product Catalog information that can be used by the Controller to load information about products
public ProductInfo findProductInfo() {
try{
String url =....;
return webClient.get().uri(url)
.accept(MediaType.APPLICATION_JSON).retrieve().
bodyToMono(ProductInfo.class).share().block();
}catch(Exception e){
log.error("Exception during retrieval of ProductInfo Data [}", e);
return null;
}
}
The findProductInfo method is wrapped in a Service which is used to retrieve ProductInfo in the Controller.
I am using share() because the block() call to the Rest controller just hangs.
However, if I simply call block() with first calling share() the call to the controller returns but throws the following error. I'm very new to using WebFlux so I have no idea what is going on. I'd appreciate some help in deciphering what is going on and a solution to this problem
java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-2
When I use share() followed by block() my application hangs when I invoke the rest endpoint. However, if I use block() alone the method returns
Solved: My job was running in a single thread, so the share().block() was blocking the main thread of the job launcher. I figured out that from observing the task executor was synchronous and that gave me the clue, it suddenly made sense. I configured a task executor so the job would run in its own thread and viola!

Quarkus: "no tenant identifier specified" in callback

I try to add multi-tenancy support for my Quarkus app, following Quarkus hibernate-orm doc (see last section).
I have my CustomTenantResolver class and configure in application.properties, with multiple data sources, but no named persistent unit, see below:
# Default data source
quarkus.hibernate-orm.datasource=master
quarkus.hibernate-orm.database.generation=none
quarkus.hibernate-orm.multitenant=DATABASE
# ----- Tenant 'master' (default) ---------------
quarkus.datasource."master".db-kind=postgresql
quarkus.datasource."master".username=postgres
quarkus.datasource."master".password=password
quarkus.datasource."master".jdbc.url=jdbc:postgresql://localhost:5432/db_master
# ----- Tenant 'test' ---------------------------
quarkus.datasource.test.db-kind=postgresql
quarkus.datasource.test.username=postgres
quarkus.datasource.test.password=password
quarkus.datasource.test.jdbc.url=jdbc:postgresql://localhost:5432/db_test
Everything works fine for Web Services APIs functions - based on incoming web service calls, I can extract and supply tenant identifier for DB access.
Problem is, my app also needs to use callback method to listen on messages coming from Apache Pulsar queue. When a message comes in and triggers this callback, any DB access in this method will give this exception:
SessionFactory configured for multi-tenancy, but no tenant identifier specified: org.hibernate.HibernateException: SessionFactory configured for multi-tenancy, but no tenant identifier specified
at org.hibernate.internal.AbstractSharedSessionContract.<init>(AbstractSharedSessionContract.java:172)
at org.hibernate.internal.AbstractSessionImpl.<init>(AbstractSessionImpl.java:29)
at org.hibernate.internal.SessionImpl.<init>(SessionImpl.java:221)
at org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl.openSession(SessionFactoryImpl.java:1282)
at org.hibernate.internal.SessionFactoryImpl.openSession(SessionFactoryImpl.java:472)
at io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.acquireSession(TransactionScopedSession.java:86)
at io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.persist(TransactionScopedSession.java:138)
at io.quarkus.hibernate.orm.runtime.session.ForwardingSession.persist(ForwardingSession.java:53)
... (snipped)
Apparently my CustomTenantResolver class was not called during this listener callback as the callback is another fresh thread, hence no tenant id is supplied.
Do I miss anything? How about the scheduler in Quarkus - how does it support multi-tenancy in scheduled jobs?
Thanks for helps.
I had a similar issue when pulling messages from JMS. The cause of the issue is that io.quarkus.hibernate.orm.runtime.tenant.HibernateCurrentTenantIdentifierResolver ( which implements CurrentTenantIdentifierResolver and as the doc says Maps from the Quarkus {#link TenantResolver} to the Hibernate {#link CurrentTenantIdentifierResolver} model ) expects a request context to be active before calling our implementation of TenantResolver, as shown here:
// Make sure that we're in a request
if (!Arc.container().requestContext().isActive()) {
return null;
}
TenantResolver resolver = tenantResolver(persistenceUnitName);
String tenantId = resolver.resolveTenantId();
I solved it on my app by, first, enabling the request context on the JMS consumer:
Arc.container().requestContext().activate();
and, second, using a ThreadLocal to "pass" the current tenant id to the TenantResolver that will be called later by Hibernate ( through the HibernateCurrentTenantIdentifierResolver instance):
CurrentTenantLocal.setCurrentTenantId("public");
On my TenantResolver ( the class that implements TenantResolver ) I resolve the tenant from either an injected JsonWebToken jwt when it comes from a WebRequest, or using the ThreadLocal when consuming from JMS:
if ( CurrentTenantLocal.getCurrentTenantId() != null ) {
return CurrentTenantLocal.getCurrentTenantId();
}
Caveats:
Note that I haven't done an exhaustive search of the possible side effects of activating the request context... but I have no problems so far.

RxJava disposing Observable/Flowable in a microservice Server project

I am using RxJava for a server microservice project, where using Jetty as HTTP Servlet server.
I am handling requests either from client or main server with Observable for different flows.
When a request hitting the api below, I will return a Response after Observable finishes the job.
#GET
#Path("{uuid}")
#Produces(MediaType.APPLICATION_JSON)
public Response doThingsForClient(#PathParam("uuid") String uuid) {
Worker worker = new Worker(uuid);
worker.run();
return Response.ok("Awesome").build();
}
class Worker {
String uuid = null;
public Worker(String uuid) {
this.uuid = uuid;
}
public void run() {
Observable.concat(Observable1,Observable2,Observable3);
}
}
I am wondering if I need to dispose these Observables or Flowables.
According to this: Does RxJava2 auto dispose observable when they call completed or error?
and the RxJava3 sourcecode, i don't think Flowable at least is disposed automatically?
If I need to manually dispose the resources,
Is it better to create a CompositeDisposable, then add disposable to the CompositeDisposable at each Observer(Observable1...Observable3)'s onSubscribe() being called, call compositeDisposable.dispose() after the concat finishes.
Should I also monitor the Jetty AbstractLifeCycle to dispose these Observables(It sounds similar as Android)? I am not sure how other people are using RxJava at the server side, open to any suggestions to these questions and general Rx approach at server projects.
Thanks!

Adding new method to ServiceEventSource disables logging (Service Fabric service)

I am trying to add new method to ServiceEventSource class in a Service Fabric service (web app, web api or stateless services), to log warnings and exceptions separately from information-type messages.
When I add new method to ServiceEventSource class, it does not output any message and this.IsEnabled() returns false. Out of the box, and if I remove newly added method, ServiceEventSource outputs messages as expected, and this.IsEnabled() returns true.
I am following Using EventSource generically sample.
For example, just adding following code will cause ServiceEventSource to stop logging:
private const int ErrorEventId = 7;
[Event(ErrorEventId, Level = EventLevel.Error, Message = "Error: {0} - {1}")]
public void Error(string error, string msg)
{
WriteEvent(ErrorEventId, error, msg);
}
I've looked everywhere and can't find any reference to this unexpected behaviour.

Hystrix getting access to the current execution state within fallback

I successfully configured spring-cloud (via spring-cloud-starter-hystrix) to wrap a call to a service.
This all works fine and looks like the following:
#Component
public class MyService {
#HystrixCommand(fallbackMethod = "fallback")
public void longRunning() {
// this could fail
}
public void fallback() {
// fallback code
}
}
My question now is, I would like to log some statistics about the execution error in longRunning()
Trying to access HystrixRequestLog.getCurrentRequest() within the fallback method throws
java.lang.IllegalStateException: HystrixRequestContext.initializeContext() must be called at the beginning of each request before RequestVariable functionality can be used.
I am looking for a simple way to log the exception of longRunning if the fallback is called.
testing with v1.0.0.RC2
To see a stack trace you can just enable DEBUG logging in com.netflix.hystrix.
As far as I can tell, to use the HystrixRequestContext the caller of MyService has to call HystrixRequestContext.initializeContext() before using the service. That sucks, so if anyone has a better idea, I'm interested.
Starting from Javanica v1.4.21, it allows fallback method to have an argument of Throwable type for accessing the command execution exception like so:
public void fallback(Throwable e) {
// fallback code
LOGGER.error(e.getMessage());
}
To get this feature, your build config needs to override the older version of Javanica pulled in by Spring Cloud.