Spring Session In Paas Sticky Session - spring-session

so I have deployed my spring app on Heroku with 2 nodes and I noticed that if I add a product in the basket and I start refreshing the cart page, sometimes I see the product, sometimes the product is not there. So I'm thinking that maybe the request is hitting different nodes every time.
On the spring-session website I can see This can make clustering much easier. This is nice because the clustering setup is done in a vendor neutral way. Furthermore, in some environments (i.e. PaaS solutions) developers cannot modify the cluster settings easily. But I couldn't find how to make the settings for a sticky session with spring-session. My setup is just the one ouf of the box:
#Bean(name = { "defaultRedisSessionRepository", "sessionRepository" })
#ConditionalOnProperty(name = { "nemesis.platform.redis.url" })
public SessionRepository defaultRedisSessionRepository(RedisTemplate<String,ExpiringSession> redisTemplate) throws Exception {
return new RedisOperationsSessionRepository(redisTemplate);
}
then in my MVC config:
#Bean(name = {"defaultSessionFilter", "sessionFilter"})
public Filter sessionFilter() {
CompositeFilter compositeFilter = new CompositeFilter();
compositeFilter.setFilters(Arrays.asList(new SessionRepositoryFilter(applicationContext.getBean("sessionRepository", SessionRepository.class)), applicationContext.getBean(UrlEncoderFilter.NAME, UrlEncoderFilter.class)));
return compositeFilter;
}
and then in my WebApplicationInitializer:
final FilterRegistration sessionFilterChainReg = servletContext.addFilter("sessionFilter", DelegatingFilterProxy.class);
sessionFilterChainReg.addMappingForServletNames(EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC, DispatcherType.FORWARD, DispatcherType.INCLUDE), false, dispatcherServletReg.getName());

Spring Session does not setup sticky sessions. Instead, it allows any JVM to lookup the session object by placing it in a central datastore (i.e. Redis).

Related

How can I activate JMX for Caffeine cache

I was looking at this SO question, but here I only want Caffeine to start reporting to JMX.
I have added an application.conf file as such and referenced it via -Dconfig.file:
caffeine.jcache {
# A named cache is configured by nesting a new definition under the caffeine.jcache namespace. The
# per-cache configuration is overlaid on top of the default configuration.
default {
# The monitoring configuration
monitoring {
# If JCache statistics should be recorded and externalized via JMX
statistics = true
# If the configuration should be externalized via JMX
management = true
}
}
It is not working, but I suspect it might be related to jcache, but not sure what is the expected way to implement this basic monitoring.
The cache instance is registered with the MBean server when it is instantiated by the CacheManager. The following test uses the programmatic api for test simplicity.
public final class JmxTest {
#Test
public void jmx() throws MalformedObjectNameException {
var config = new CaffeineConfiguration<>();
config.setManagementEnabled(true);
config.setStatisticsEnabled(true);
var manager = Caching.getCachingProvider().getCacheManager();
var cache = manager.createCache("jmx", config);
cache.put(1, 2);
var server = ManagementFactory.getPlatformMBeanServer();
String name = String.format("javax.cache:type=%s,CacheManager=%s,Cache=%s",
"CacheStatistics", manager.getURI().toString(), cache.getName());
var stats = JMX.newMBeanProxy(server, new ObjectName(name), CacheStatisticsMXBean.class);
assertThat(stats.getCachePuts()).isEqualTo(1);
}
}
If you do not need JCache for an integration then you will likely prefer to use the native APIs and metrics library. It is supported by Micrometer, Dropwizard Metrics, and the Prometheus client. While JCache is great for framework integrations, its api is rigid and cause surprising performance issues.

How to configure gemfire as message store for aggregator module in spring cloud data flow

Link - https://github.com/spring-cloud-stream-app-starters/aggregator/tree/master/spring-cloud-starter-stream-processor-aggregator does not list property for gemfire message store
The GemfireMessageStore is configured like this:
#ConditionalOnClass(GemfireMessageStore.class)
#ConditionalOnProperty(prefix = AggregatorProperties.PREFIX,
name = "message-store-type",
havingValue = AggregatorProperties.MessageStoreType.GEMFIRE)
#Import(ClientCacheAutoConfiguration.class)
static class Gemfire {
#Bean
#ConditionalOnMissingBean
public ClientRegionFactoryBean<?, ?> gemfireRegion(GemFireCache cache, AggregatorProperties properties) {
ClientRegionFactoryBean<?, ?> clientRegionFactoryBean = new ClientRegionFactoryBean<>();
clientRegionFactoryBean.setCache(cache);
clientRegionFactoryBean.setName(properties.getMessageStoreEntity());
return clientRegionFactoryBean;
}
#Bean
public MessageGroupStore messageStore(Region<Object, Object> region) {
return new GemfireMessageStore(region);
}
}
The point is that you always can override that ClientRegionFactoryBean with your own.
Or you can take into account that ClientCacheAutoConfiguration is based on the #ClientCacheApplication, which, in turn, allows you to have a ClientCacheConfigurer bean and provide whatever is sufficient for your client cache configuration. Including config and pool. That's right: it is not on the app starter configuration level and you have to right some custom code to be included as a dependency into the final uber jar for target binder-specific application.
More info how to build them is here in Docs: https://docs.spring.io/spring-cloud-stream-app-starters/docs/Einstein.RC1/reference/htmlsingle/#_patching_pre_built_applications
A variety of backend storage options exist through Spring Integration. You can read more about it in spring-cloud-starter-stream-processor-aggregator/README.
Spring Integration docs on this matter are included as a link, and the Gemfire section could be useful.
It'd be also useful to review MessageGroupStore implementation, since it is the foundation for the storage option in aggregator.

OWIN asynchronous startup (using Hangfire)

I am using Hangfire with SQL Storage on a remote SQL server and running it alongside my existing MVC site. My startup class is very simple:
public void Configuration(IAppBuilder app)
{
app.UseHangfire(config =>
{
config.UseSqlServerStorage("MY_CONNECTION_STRING");
config.UseServer();
});
}
The problem is that any delay in connecting to the remote server delays my MVC site from spinning up. Is there a way to start OWIN asynchronously so that the project is able to respond to requests regardless of what happens during the OWIN startup, including fatal errors?
Hangfire initialization logic is performed in a dedicated thread to decrease the start-up time of your application. So, UseServer method creates a new thread only, without any additional logic.
UseSqlServerStorage method connects to your database to check your current schema to run automatic migrations if necessary (one simple query to the Hangfire.Schema table). This is the default behavior, however you are able to disable it:
var options = new SqlServerStorageOptions
{
PrepareSchemaIfNecessary = false
};
var storage = new SqlServerStorage("<name or connection string>", options);
After performing this step, Hangfire will not connect to your database at startup (and no other class will do it). But keep an eye on release notes, they will contain information about database storage changes.

Multi-tenant ServiceStack API, same deployment to respond to requests on different hostnames?

We're creating APIs using ServiceStack that are multi-tenant. We want to do DNS-based load-balancing and routing, rather than stitch things up via a reverse proxy (like nginx or haproxy).
We have Request DTOs that have a Tenant parameter. ServiceStack (and its SwaggerFeature) allow us to define custom routes, and document the DTOs such that we can read values from path, query, headers, or body.
How do we (best) wire things so that DTO properties can read values from a hostname pattern as well? So, make the Route take values from matching out of the hostname as well as the path?
We'd like to have URLs like
https://{tenant}.{DNS zone for environment}/{rest of path with tokens}
Also - out DNS zone will vary depending which environment we're in - for non-production we use (say) testing-foobar.com, and production we use real-live.com. Ideally we'd be able to support both with a single route declaration (and we prefer decorating the Request DTO instead of imperative declaration at run-time AppHost.Init).
I solved this just this week, on a existing multi-tenant system which uses .NET security principals to deal with the user permissions and tenants. I used a custom ServiceRunner to select the tenant and set up the security. Your approach to multi-tenant is different, but using a ServiceRunner still seems a valid approach.
You'd end up with something like this:
public class MyServiceRunner<T> : ServiceRunner<T>
{
public MyServiceRunner(IAppHost appHost, ActionContext actionContext)
: base(appHost, actionContext)
{}
public override void BeforeEachRequest(IRequestContext requestContext, T request)
{
// Set backend authentication before the requests are processed.
if(request instanceof ITenantRequest)
{
Uri uri = new Uri(requestContext.AbsoluteUri);
string tenant = uri.Host; // Or whatever logic you need...
((ITenantRequest).Tenant = tenant;
}
}
}
public class MyAppHost : AppHostBase
{
public MyAppHost() : base("My Web Services", typeof(MyService).Assembly) { }
public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(ActionContext actionContext)
{
return new MyServiceRunner<TRequest>(this, actionContext);
}
public override void Configure(Container container)
{
...
}
}
Perhaps the Requests filtering approach is somehow better, but this does the job for us.

Multi-Tenant w/ NHibernate+Castle Windsor (Single App, Multiple DBs)

Creating a multi-tenant asp.net mvc 3 app with single app instance/multiple databases per tenant. There will also be a separate 'master' database that will house tenant-specific info (enabled features, tenant db connection info etc). Newbie to both NHibernate & IOC (Castle Windsor) and used this tutorial to get a basic CRUD setup going.
The following is what I use (from above-mentioned tutorial) to 'use' NHibernate:
public class PersistenceFacility : AbstractFacility
{
protected override void Init()
{
var config = BuildDatabaseConfiguration();
Kernel.Register(
Component.For<ISessionFactory>()
.UsingFactoryMethod(config.BuildSessionFactory),
Component.For<ISession>()
.UsingFactoryMethod(k => k.Resolve<ISessionFactory>().OpenSession())
.LifeStyle.PerWebRequest);
}
private Configuration BuildDatabaseConfiguration()
{
return Fluently.Configure()
.Database(SetupDatabase)
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<SectionMap>()
.Conventions.AddFromAssemblyOf<TableNameConvention>();
})
.ExposeConfiguration(ConfigurePersistence)
.BuildConfiguration();
}
protected virtual AutoPersistenceModel CreateMappingModel()
{
var m = AutoMap.Assembly(typeof(EntityBase).Assembly)
.Where(IsDomainEntity)
.OverrideAll(ShouldIgnoreProperty)
.IgnoreBase<EntityBase>();
return m;
}
protected virtual IPersistenceConfigurer SetupDatabase()
{
return MsSqlConfiguration.MsSql2008
.DefaultSchema("dbo")
.UseOuterJoin()
.ProxyFactoryFactory(typeof(ProxyFactoryFactory))
.ConnectionString(x => x.FromConnectionStringWithKey("MasterDB"))
.ShowSql();
}
protected virtual void ConfigurePersistence(Configuration config)
{
SchemaMetadataUpdater.QuoteTableAndColumns(config);
}
protected virtual bool IsDomainEntity(Type t)
{
return typeof(EntityBase).IsAssignableFrom(t);
}
private void ShouldIgnoreProperty(IPropertyIgnorer property)
{
property.IgnoreProperties(p => p.MemberInfo.HasAttribute<DoNotMapAttribute>());
}
}
The approach I'm thinking of taking is that the application would look at the host header/url to determine the tenant and then query the 'master' db to get the respective tenant's database connection info. I'm guessing the approach I have to take is have a separate SessionFactory per client - only problem is I don't know how (and where) to integrate it. Would appreciate any help/pointers to get a better understanding of how to [a] resolve this problem [b] get a better understanding of how to use Castle Windsor. Apologies, as the castle site seems to be a great resource, but not easily understood by a newbie like me.
Thanks!
Environment: ASP.NET MVC 3, .NET 4, Castle Windsor + Fluent NHibernate + NHibernate (via NuGet)
usually the approach that's taken is to have multiple session factories (one per tenant) and use IHandlersSelector to pick the right one based on some data from the request.
Regarding the remark about documentation being not easily understood, we're keen to improve it if you'd like to point out parts that you found not too easy to follow.

Resources