Hibernate Concurrent ManyToMany relationship - spring

I am having troubles when running last test against a Kotin Spring Boot application. One critical object has a ManyToMany relationship to another one:
#Entity
data class Subscriber(
#ManyToMany
#JoinTable(name = "subscriber_message",
joinColumns = [JoinColumn(name = "subscriber_id")],
inverseJoinColumns = [JoinColumn(name = "message_id")],
indexes =[
Index(name = "idx_subscriber_message_message_id", columnList = "message_id"),
Index(name = "idx_subscriber_message_subscriber_id", columnList = "subscriber_id")
]
)
val messages: MutableList<Message>
}
#Entity
data class Message(
)
Messages are added to a subscriber like this:
subscriber.messages.add(message)
save(subscriber)
When a new message arrives, the controller asks the repository to add the message to all existing subscribers by calling the following method:
#Synchronized
fun SubscriberRepository.addMessage(message: Message) {
findAll().forEach {
it.messages.add(message)
save(it)
}
}
We currently use MutableList for this property since we need to add new elements to the list. This type is not thread safe, so I tried the good old Java concurrent set java.util.concurrent.ConcurrentSkipListSet, to which Spring complained saying that this type is not supported.
Is there a better way to stay thread safe in a web application regarding such a relationship. The list of messages is consumed by a subscriber who then clears it from messages it is done with. But because of concurrency, the cleaning process doesn't work either!

I don't think JPA is going to allow it to be you to initialise it with any concurrent versions because hibernate has own implementations like PersistentSet etc for each collection type which it supports.
When adding a message, you already have synchronised method so that should be fine.
Now I guess many threads trying to consume the messages from the retrieved subscriber. So why don't you do modify the subscriber like this before giving it to the threads consume it.
retrieve subscriber with messages
subscriber.setMessages(Collections.synchronizedList(subscriber.getMessages())) and give it to threads (I don't know what is the equivalent in kotlin)
So now subscriber.messages.removeAll(confirmedMessages) will be thread safe

Related

Spring RabbitMQ Retry policy only on a specific listener

I would like to have a Retry Policy only on a specific listener that listen to a specific queue (DLQ in the specific case).
#RabbitListener(queues = "my_queue_dlq", concurrency = "5")
public void listenDLQ(Message dlqMessage) {
// here implement logic for resend message to original queue (my_queue) for n times with a certain interval, and after n times... push to the Parking Lot Queue
}
BUT if I am not misunderstood when I specify a Retry Policy (for ex. on the application.yaml) all #RabbitListeners use it.
I suppose the only way would be to create a dedicated container factory, but this last one would be identical to the default one with ONLY one more Retry policy ... and it doesn't seem to me like the best to do so.
Something like that :
#RabbitListener(containerFactory = "container-factory-with-retrypolicy", queues = "myDLQ", concurrency = "5")
public void listenDLQ(Message dlqMessage) {
// here implement logic for resend message to original queues for n times with a certain interval
}
Do you see alternatives ?
Thank you in advance.
The ListenerContainer instances are registered to the RabbitListenerEndpointRegistry. You can obtain a desired one by the #RabbitListener(id) value. There you can get access to the MessageListener (casting to the AbstractAdaptableMessageListener) and set its retryTemplate property.
Or you can implement a ContainerCustomizer<AbstractMessageListenerContainer>, check its getListenerId() and do the same manipulation against its getMessageListener().

Spring ACL with Kotlin Exposed

I'm using kotlin exposed and spring acl: JdbcMutableAclService.
The dummy code looks like:
transaction {
//operation 1
dao.updateSomething(resourceId)
val sids = dao.getUserIdsByResourceId(resourceId)
//operation 2
val pObjectIdentity = ObjectIdentityImpl(PROJECT, resourceId)
val pMutableAcl = aclService.readAclById(pObjectIdentity) as MutableAcl
var i = pMutableAcl.entries.size
sids.forEach {
pMutableAcl.insertAce(i++, BasePermission.READ, PrincipalSid(it), true)
}
aclService.updateAcl(pMutableAcl)
//operation 3
val rObjectIdentity = ObjectIdentityImpl(RESOURCE, resourceId)
val rMutableAcl = aclService.readAclById(rObjectIdentity) as MutableAcl
var i = rMutableAcl.entries.size
sids.forEach {
rMutableAcl.insertAce(i++, BasePermission.READ, PrincipalSid(it), true)
}
aclService.updateAcl(rMutableAcl)
}
If something happens in operation 3 - it won't write nothing to db, the outer transaction will also rolled back, and operation 1 won't be committed as well.
Unfortunately operation 2 won't be rolled back.
So my assumption every time of using updateAcl it creates its own isolated transaction.
I don't know how it work in case of Spring Jpa, and #Transactional annotation (is JdbcMutableAclService take into consideration outer transaction or not), but in case of Exposed it is not.
Is it correct behaviour at all? Should every acl update be an isolated transaction?
Is there a way to integrate Exposed and JdbcMutableAclService without implementing my own MutableAclService?
UPD for #Tapac
I'm using org.jetbrains.exposed:exposed-spring-boot-starter without any additional configuration, so based on ExposedAutoConfiguration it is org.jetbrains.exposed.spring.SpringTransactionManager.
But during the debugging i saw in stacktrace some refs to ThreadLocalTransactionManager.
And don't know is it useful information, but i don't use spring transaction annotation and instead of that i use exposed transaction{} block.

ReactiveCrudRespository never returns from SaveAll

At startup, I check for some data and if not present attempt to save some defaults (temporarily for testing).
val subs = repo.findAll().toIterable()
if(subs.none()) {
repo.saveAll(defaults.map { Source(it.link.hashCode().toLong(), it::class.java.canonicalName, arrayOf(it.link)) }).blockLast()
}
On the first run, we will reach the saveAll() but never unblock. The data is saved in MongoDB, and I can confirm it with Robo 3t.
Subsequent runs with data actually present will lead to the first findAll never unblocking.
Profiling in MongoDB appears to show a successful query.
Profile of findAll() query
My Respository and Entity are as follows:
interface SourceRepository : ReactiveCrudRepository<Source, Long> {
//
}
data class Source(
#Id val id: Long,
val type: String,
val params: Array<String>
)
This is in Kotlin, against Spring Boot 2.0.0.M4. I am targeting a MongoDB instance running in docker. If I remove this bit of startup logic, my other ReactiveCrudRepository is able to read/write just fine, never blocking.
The working Repository's saveAll() call also concludes in a blockLast(), as I found that without this the save would never actually occur.

What's the purpose of LoggingChannel.Level

I'm trying to understand the proper way to use Windows.Foundation.Diagnostics.LoggingChannel. In particular I'd like to understand the purpose behind the Level property and when is this property set.
As described in the MSDN documentation of LoggingChannel, the Level property is read-only. So how can I set the level that a channel accepts messages at?
Currently what I have designed as a logger for my app is something like below:
public class Logger
{
public LoggingLevel LoggerLoggingLevel { get; set; }
private LoggingSession _session;
private LoggingChannel _channel;
public Logger()
{
_channel = new LoggingChannel("MyChannel");
_session = new LoggingSession("MySession");
_session.AddLoggingChannel(_channel);
}
public void LogMessage(string msg, LoggingLevel level)
{
if (level >= LoggerLoggingLevel)
{
_channel.LogMessage(msg, level);
}
}
.
.
.
}
// The consumer of the Logger class will instantiate an instance of it,
// sets the LoggerLoggingLevel, and then starts logging messages at various levels.
// At any point, the consumer can change LoggerLoggingLevel to start accepting
// messages at different levels.
IS this the right approach or is there a better way (for example by somehow setting the level of _channel and then passing the message & level to the channel, letting the channel decide whether it should filter out the message or accept and log it)?
LoggingChannel.Level tells you "somebody has expressed interest in receiving messages from you that are of severity 'Level' or higher". This property will be set automatically by the runtime when somebody subscribes to events from your LoggingChannel instance. (Within your app, you can subscribe to your app's events using the LoggingSession class; outside of your app, you can record your app's events using a tool like tracelog or xperf.)
In simple scenarios, you don't need to worry about the value of LoggingChannel.Level. The LoggingChannel.LogMessage function already checks the value of LoggingChannel.Level. It also checks the value of LoggingChannel.Enabled, which tells you whether anybody is subscribed to your events at any level. (Note that the value of LoggingChannel.Level is UNDEFINED and MEANINGLESS unless LoggingChannel.Enabled is true.) In normal use, you don't need to worry about LoggingChannel.Enabled or LoggingChannel.Level -- just call LogMessage and let LoggingChannel check the levels for you.
LoggingChannel exposes the Enabled and Level properties to support a more complex scenario where it is expensive to gather the data you are about to log. In this case, you would probably like to skip gathering the data if nobody is listening for your event. You would then write code like this:
if (channel.Enabled && channel.Level <= eventLevel)
{
string expensiveData = GatherExpensiveData();
channel.LogMessage(expensiveData, eventLevel);
}
Note that the Windows 10 version of LoggingChannel added a bunch of new methods to make life a bit easier. If your program will run on Windows 10 or later, you can use the IsEnabled method instead of separate checks for Enabled and Level:
if (channel.IsEnabled(eventLevel))
{
string expensiveData = GatherExpensiveData();
channel.LogMessage(expensiveData, eventLevel);
}
A bunch of other stuff was also added to LoggingChannel for Windows 10. You can now log complex events (strongly-typed fields) instead of just strings, you can define keywords and opcodes (look up ETW documentation for more information), and you can basically have your LoggingChannel act like a first-class ETW citizen.

TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing

I know this question has been asked numerous times but I couldn't find a suiting answer for me.
I've got two entities in a Spring Roo-application which are in a Many-To-Many-relationship, Release and Component.
First I fetch an instance of an existing Release via
selectedRelease = Release.findReleasesByReleaseNumberEquals(version).getSingleResult();
The method above is a Roo-generated finder which looks like this:
public static TypedQuery<Release> Release.findReleasesByReleaseNummerEquals(String releaseNumber) {
EntityManager em = Release.entityManager();
TypedQuery<Release> q = em.createQuery("SELECT o FROM Release AS o WHERE LOWER(o.releaseNumber) LIKE LOWER(:releaseNummer)", Release.class);
q.setParameter("releaseNumber", releaseNumber);
return q;
}
Then I create a new instance of Component and try to assign it to the selected Release
Component component = new Component();
Set<Release> releases = new HashSet<Release>();
releases.add(selectedRelease);
component.setReleases(releases);
component.persist();
Upon trying to execute persist() I get the exception:
TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.Release;
Does anyone have advice regarding this problem?
The mappings look like this:
Release.java:
#ManyToMany(cascade = CascadeType.PERSIST, mappedBy = "releases")
private Set<Component> components = new HashSet<Component>();
Component.java
#ManyToMany(cascade=CascadeType.PERSIST)
private Set<Release> releases = new HashSet<Release>();
The message is clear: you are trying to save an object, component, that references another object that hasn't been saved yet, selectedRelease.
The solution seems to be easy: just save (and flush whenever you want the changes be comunicated to the database) the Release objects before saving the Component.
However, JPA allows you to avoid all these sentences with TRANSITIVE PERSISTENCE, that is, the cascade options in the #...ToMany annotations.
Just a warning in case you have a bidirectional relationship, when you have a mappedBy attribute in one of the #...ToMany associations. In this case, you'll need a convenient method to manage the association, that is, to set both sides of the association.
For instance, an addRelease(Release r) in Component that both adds the release to the component's set, and sets the component (actually, the this instance) to the release passed as parameter.

Resources