Blogpost in the making: Spring and visibility problems

Pagina: 1
Acties:
  • 162 views sinds 30-01-2008
  • Reageer

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Ik ben op dit moment bezig om een blogpost te schrijven over visibility problems in Spring based applications.

Ik ben benieuwd wat jullie er van vinden en of jullie het begrijpen. En ik wil uiteraard ook vragen over de technische problemen erachter

Het stuk is trouwens nog lang niet klaar (kromme zinnen, stukken nog niet af, redenaties veel te kort etc), maar de kern van het verhaal staat er; alle belangrijke ingredienten zitten eirn.


++ Spring and visibility problems

Abstract

Spring is a great framework, but I expect that a lot of Spring based applications, could be subject to a specific concurrency problem: visibility [todo]. This blogentry describes the cause and effects of this problem, and also how it can be solved.

Visibility problems

Most programmers don't have much experience with multi-threading, and most have a very simplistic view on reality: if a thread makes a change to a variable, this change directly is visible to all other threads. This view is called sequential consistency, but the problem is that no virtual machine supports it. The reason why sequential consistency is not supported, is that it greatly reduces performance. If every change of a variable should be visible to all threads, a lot of optimizations can't be used. To name a few:
  1. caches for multi-core/multi-cpu systems, would be useless, because a stale value is not acceptable.
  2. registers can't be used to function as (a very fast) local copy of a variable, because a change to a register is not visible in other threads.
The effect of a visibility problem, is that a value written to a variable by one thread, maybe is not visible in other threads. A possible consequence of this effect, could be as simple as a NullPointerException, when a variable still contains a null value. NullPointerExceptions are 'great' because the system fails fast and hard. But visibility problems can also lead to much harder to detect problems, for example a default value that is not overwritten. Visibility problems can lead to errors and unpredictable behavior, and you definitely want to keep them out of your system. The root cause of visibility problems is that variables are not published safely. This can be done in many ways, to name a few:
  1. make field volatile
  2. make field final
  3. use in synchronized context
There are more, see the happens before rules in JCiP [todo]

Spring based applications and visibility problems

I expect that a lot of Spring based applications, are subject to visibility problems. A lot of beans are singletons: dao's, controllers, managers etc, and these singletons often are shared by many threads, eg:
  • threads from the servlet container
  • threads from remoting middleware
  • threads from jmx
But sharing objects by multiple threads, is one of the ingredients for visibility problems. Example:
public class EmployeeManagerImpl implements EmployeeManager{

    private EmployeeDao employeeDao;
    
    public setEmployeeDao(EmployeeDao employeeDao){
        this.employeeDao = employeeDao;
    }
    
    public void fire(long employeeId){
        Employee employee = employeeDao.load(employeeId);
        employee.fire();
    }
}
And the bean configuration that belongs to it:
    
        
    
The visibility problem here is that the reference to the employeeDao maybe not is visible in a thread that uses the employeeDao, and this could lead to a NullPointerException. The problem can be solved in a few ways:
  1. Make all fields volatile. Although it sounds quite strange, because a value is not going to change after it has been set, a volatile field safely publishes the value. Personally I don't like this solution very much
  2. Make the fields final because it also safely publishes the value. The problem with final fields is that they only can be set by constructor injection and Spring promotes setter injection. Personally I never liked setter injection (although it prevents complex constructors) because it is hard to understand what can and can't change. Especially with more complex objects, you don't want to worry about changing fields and maintaining invariants.
oorzaak van het probleem zit hem in unsafe publication van velden.

Problem not that bad?

The visibility problems are not that bad under virtual machines that support JSR133 (like Java 5). This is because they support a mechanism called "Piggybacking on Synchronization" bad name, mention tim peierls: save handoff, save passing). Piggybacking on Synchronization uses the safe publication of a variable X, to safely publish all unsafely-published-variables that are changed before X. In this case the placement in the Map where the beans are stored, provides this memory barrier, and that is why beans, that have visibility problems themselfes, are safely published. org.springframework.beans.factory.support.DefaultSingletonBeanFactory
protected void addSingleton(String beanName, Object sharedBean) {
    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(sharedBean, "Singleton object must not be null");

    synchronized (this.singletonCache) {
        this.singletonCache.put(beanName, sharedBean);
    }
}
The unlock of the monitor-lock of the singletonCache is the reason why piggybacking works (it is a happens-before rule, see JCiP): Todo: gebruik je die lock ook zo snel mogelijk weer als je iets uit de map wilt halen? Personally I'm not too happy with this side effect because I don't know Another problem is that it only works on virtual machines that support JSR133. On virtual machines that don't support JSR133, the memory model is undefined. So it is guessing if an application is threadsafe.

todo: hoe zit het met de niet singleton beans?

[ Voor 7% gewijzigd door een moderator op 23-11-2006 14:42 ]


  • den 150
  • Registratie: Oktober 2002
  • Niet online
Ik snap het probleem niet zo goed.

Ik neem aan dat je de volgende config bedoelt:
XML:
1
2
3
<bean id="employeeManager" class="EmployeeManagerImpl">
    <property name="employeeDao" ref="databaseEmployeeDao"/>
</bean>


Als ik het goed begrijp wil je zeggen dat Thread1 dit perfect kan doen
Java:
1
employeeManager.fire(1l);

maar dat door het ontbreken synchronisatie (op bean niveau dan toch) ervoor kan zorgen dat Thread2 bij dezelfde code een NPE krijgt omdat employeeDao wel geset is maar nog niet zichtbaar in het private field van EmployeeDaoImpl. Zie ik dat juist?

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
den 150 schreef op donderdag 23 november 2006 @ 21:20:

maar dat door het ontbreken synchronisatie (op bean niveau dan toch) ervoor kan zorgen dat Thread2 bij dezelfde code een NPE krijgt omdat employeeDao wel geset is maar nog niet zichtbaar in het private field van EmployeeDaoImpl. Zie ik dat juist?
Helemaal correct. Dat is dus het nare met visibility problemen :) Probleem doet zich minder voor op systemen die jsr-133 ondersteunen (java 5 doet dit), maar imho is het nog steeds vragen om problemen. In systemen die jsr-133 nog niet ondersteunen, worden er geen garanties afgegeven.

ps
Ik neem aan dat je EmployeeManagerImpl bedoelt ipv EmployeeDaoImpl.

[ Voor 25% gewijzigd door Alarmnummer op 24-11-2006 08:03 ]


  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

Als ik jegoed begrijp, dan kan je het beestje een duidelijkere naam kan geven: "het Spring framework is niet thread-safe" ;). Dat is geen probleem zolang je geen threads gebruikt op een single core systeem en als je dat wel doet, dan draag je als het goed is zelf zorg voor thread safety, maar dat wordt met multi-core systemen automatisch ieders probleem.

Overigens, "Personally I don't like this solution very much" is natuurlijk onnacceptabel :). Je hebt ofwel een goede (technische) reden om die oplossing niet prettig te vinden of je laat het weg; deze formulering roept vragen op.

Wie trösten wir uns, die Mörder aller Mörder?


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Confusion schreef op vrijdag 24 november 2006 @ 08:26:
Als ik jegoed begrijp, dan kan je het beestje een duidelijkere naam kan geven: "het Spring framework is niet thread-safe" ;). Dat is geen probleem zolang je geen threads gebruikt op een single core systeem en als je dat wel doet, dan draag je als het goed is zelf zorg voor thread safety, maar dat wordt met multi-core systemen automatisch ieders probleem.
Het is niet zozeer dat het spring framework niet threadsafe is, alhoewel:

http://opensource.atlassi...ts/spring/browse/SPR-2870

Maar zo lang jij je beans goed in elkaar zet, dus constructor icm final velden, is er niets aan de hand. Dus de threadonveiligheid ligt dus eigelijk aan de programmeur zelf en niet zozeer aan het framework.
Overigens, "Personally I don't like this solution very much" is natuurlijk onnacceptabel :). Je hebt ofwel een goede (technische) reden om die oplossing niet prettig te vinden of je laat het weg; deze formulering roept vragen op.
Uiteraard. Ik heb in de nieuwe versie dit hele stuk al zwaar verbouwd.

  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

Alarmnummer schreef op vrijdag 24 november 2006 @ 08:37:
Maar zo lang jij je beans goed in elkaar zet, dus constructor icm final velden, is er niets aan de hand.
Maar Spring promoot dependency injection via setters, waarmee beans dus per definitie al niet 'goed' zijn. Daar wil je de aandacht op vestigen?

Wie trösten wir uns, die Mörder aller Mörder?


  • Antediluvian
  • Registratie: Maart 2002
  • Laatst online: 14-11 12:58
Alarmnummer schreef op donderdag 23 november 2006 @ 14:18:
<h3>Spring based applications and visibility problems</h3>
........
Persoonlijk ben ik nog nooit tegen dit probleem aangelopen. Ik werk momenteel aan een vrij grote (+500 bean declaraties) JEE applicatie icm Spring. Alles wordt mooi ingeladen tijdens het opstarten van de application server en we hebben dan ook nooit de visibility problemen gehad die je in je voorbeeld beschrijft. We gebuiken trouwens enkel setter injection (en autowire byName maar dit doet er niet toe). Alles draait op een 1.4.2 VM.
Misschien dat dergelijk probleem wel geregeld voorkomen in een multi threaded rich client maar op een application server waar alles mooi op voorhand wordt ingeladen is een dergelijk probleem mij onbekend.

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Antediluvian schreef op vrijdag 24 november 2006 @ 10:42:
[...]

Persoonlijk ben ik nog nooit tegen dit probleem aangelopen. Ik werk momenteel aan een vrij grote (+500 bean declaraties) JEE applicatie icm Spring. Alles wordt mooi ingeladen tijdens het opstarten van de application server en we hebben dan ook nooit de visibility problemen gehad die je in je voorbeeld beschrijft.
Dat klopt. Ik ben het probleem zelf ook nog niet tegengekomen. Maar door de toename van multi-cores en minder strenge memory coherence regels (die regels gaan versoepeld worden naarmate het aantal cores groter gaat worden) loop je dus meer kans dat dit soort problemen kunnen optreden.

De applicatie houd zich gewoon niet aan de specs en dat betekend dat de vm geen garanties kan geven mbt correct funtioneren: misschien werkt het vandaag, en breekt het 'morgen'.
We gebuiken trouwens enkel setter injection (en autowire byName maar dit doet er niet toe). Alles draait op een 1.4.2 VM.
Misschien dat dergelijk probleem wel geregeld voorkomen in een multi threaded rich client maar op een application server waar alles mooi op voorhand wordt ingeladen is een dergelijk probleem mij onbekend.
Bijna iedere serverside app is multithreaded.

En het is misshien ook wel 'zoeken' naar problemen aangezien ik ook nog niet veel mensen heb gehoord met dit soort klachten. Maar feit is wel dat het fout kan gaan omdat de applicatie zich niet aan de speelregels van de JMM houd. En verder is concurrency control mijn stokpaardje :)

[ Voor 3% gewijzigd door Alarmnummer op 24-11-2006 11:35 ]


  • Cuball
  • Registratie: Mei 2002
  • Laatst online: 28-11 14:44
Je post lijkt wel allemaal te kloppen, maar uit ervaring weet ik dat er bij ons zeker geen tijd is om in een project dergelijke problematiek te gaan onderzoeken en analyseren laat staan oplossen, zeker omdat het "voorlopig" nog geen issue is! Zeker omdat we zo'n problemen nog nooit voorgehad hebben.

"Live as if you were to die tomorrow. Learn as if you were to live forever"


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Cuball schreef op vrijdag 24 november 2006 @ 11:28:
Je post lijkt wel allemaal te kloppen, maar uit ervaring weet ik dat er bij ons zeker geen tijd is om in een project dergelijke problematiek te gaan onderzoeken en analyseren laat staan oplossen, zeker omdat het "voorlopig" nog geen issue is! Zeker omdat we zo'n problemen nog nooit voorgehad hebben.
Het probleem kan eenvoudig gefixt worden door je objecten gewoon goed in elkaar te zetten: setter DI bad, constructor-DI (met final velden) good. En verder vind ik niet dat dit soort synchronisatie geen magische eigenschap moet zijn van je omgeving (bedoelt/onbedoelt en ook nog eens afhankelijk van een vm versie), maar dat je dit alleen zelf goed kan doen.

[ Voor 15% gewijzigd door Alarmnummer op 24-11-2006 12:12 ]


  • Antediluvian
  • Registratie: Maart 2002
  • Laatst online: 14-11 12:58
Alarmnummer schreef op vrijdag 24 november 2006 @ 11:34:

Het probleem kan eenvoudig gefixt worden door je objecten gewoon goed in elkaar te zetten: setter DI bad, constructor-DI (met final velden) good.
In principe is constructor-DI idd beter maar in praktijk niet altijd werkbaar. Ik zit bv met Manager beans waar er meer dan 15 andere dependencies geïnjecteerd worden. Een dergelijk constructor wil ik niet hebben. Volgens mij een ware hel als je daarvoor een unit test wil schrijven.

  • den 150
  • Registratie: Oktober 2002
  • Niet online
Dit lijkt mij hard op het singleton locking probleem. Single CPU systemen zullen hier nooit problemen mee hebben, multi CPU systemen theoretisch wel, in de praktijk misschien 1/1miljoen. Bovendien zijn er werkzame oplossingen die je al zelf vermeldt. Ik maak er mij iig niet druk over ;)

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Antediluvian schreef op vrijdag 24 november 2006 @ 12:18:
In principe is constructor-DI idd beter maar in praktijk niet altijd werkbaar. Ik zit bv met Manager beans waar er meer dan 15 andere dependencies geïnjecteerd worden.
Ik denk dat je dan de vraag moet stellen of je manager misschien wel niet te veel responsibilities heeft.

  • Antediluvian
  • Registratie: Maart 2002
  • Laatst online: 14-11 12:58
Alarmnummer schreef op vrijdag 24 november 2006 @ 11:16:
Dat klopt. Ik ben het probleem zelf ook nog niet tegengekomen. Maar door de toename van multi-cores en minder strenge memory coherence regels (die regels gaan versoepeld worden naarmate het aantal cores groter gaat worden) loop je dus meer kans dat dit soort problemen kunnen optreden.

De applicatie houd zich gewoon niet aan de specs en dat betekend dat de vm geen garanties kan geven mbt correct funtioneren: misschien werkt het vandaag, en breekt het 'morgen'.
Ik heb het hier even nagevraagd en onze applicatie draait in productie op 2 machines (loadbalanced) met elk 12 cpu's. Tot nu toe geen enkel probleem omtrent concurrency en variable visibility.
Alarmnummer schreef op vrijdag 24 november 2006 @ 13:20:
Ik denk dat je dan de vraag moet stellen of je manager misschien wel niet te veel responsibilities heeft.
Jammer genoeg kan dit door de complexiteit niet opgedeeld worden :'( De manager waarover is spreek is verantwoordelijk voor slechts 1 domain object + zijn relaties. Maar met een DOM van ± 250 objecten en ± 200 relaties tussen die objecten is het soms echt huilen.

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Van welke vm maak je gebruik? Ik zal kijken hoeveel informatie ik kan krijgen over het piggy backing in oudere vm's. Ik heb begrepen dat het daar niet gesupport is, en dat vm maken naar eigen wens iets mogen doen.

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
den 150 schreef op vrijdag 24 november 2006 @ 13:05:
Dit lijkt mij hard op het singleton locking probleem. Single CPU systemen zullen hier nooit problemen mee hebben, multi CPU systemen theoretisch wel, in de praktijk misschien 1/1miljoen. Bovendien zijn er werkzame oplossingen die je al zelf vermeldt. Ik maak er mij iig niet druk over ;)
Ook singlecores hebben last van double checked locking problematiek.

De instance zou in een register gezet kunnen worden die niet zichtbaar is binnen een andere thread -> dus je hebt nog steeds het probleem. Het probleem zit hem in visibility (of het gebrek aan) en reorderings en dat kan zich ook uitstekend voordoen op een singlecore.

Alleen op multicores heb je vaker gescheiden caches (vaak heeft de iedere core zijn eigen L1 cache ) en dat zorgt er dus voor dat een wijziging binnen 1 thread, niet automatisch zichtbaar is binnen een andere. Op een singlecore zou een change in de L1 cache ook zichtbaar zijn voor andere threads. Daarom hebben multi-cores nog meer last van visibility problemen dan single-cores.

[ Voor 13% gewijzigd door Alarmnummer op 25-11-2006 06:33 ]


  • Antediluvian
  • Registratie: Maart 2002
  • Laatst online: 14-11 12:58
Alarmnummer schreef op zaterdag 25 november 2006 @ 05:47:
Van welke vm maak je gebruik? Ik zal kijken hoeveel informatie ik kan krijgen over het piggy backing in oudere vm's. Ik heb begrepen dat het daar niet gesupport is, en dat vm maken naar eigen wens iets mogen doen.
WebSphere Platform 5.1
Java version = J2RE 1.4.1 IBM

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Het duurde even, maar ik heb eindelijk afgerond en geplaatst op de company website:

http://blog.xebia.com/200...-and-visibility-problems/

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Antediluvian schreef op woensdag 29 november 2006 @ 15:05:
[...]

WebSphere Platform 5.1
Java version = J2RE 1.4.1 IBM
Door de sterke cache coherence van meeste systemen, zal het meestal wel goed gaan.

  • Apache
  • Registratie: Juli 2000
  • Laatst online: 18-11 22:50

Apache

amateur software devver

Ik zeg misschien iets doms maar, die setters worden toch slechts opgeroepen bij het tot leven brengen van de bean, wanneer de methodes van de bean zoals employeeManager.fire(int) worden aangeroepen is dit toch al lang achter de rug en heeft bean al lang de dao ter zijner beschikking?

Of er moet een request binnenkomen gelijktijdig met de initialisatie maar volgens mij worden die sowieso uitgesteld tot de application gestart (en dus de dependencies geinject) zijn, weet ik uit ervaring van ongeduldig f5 geram terwijl tomcat de app aan het starten is (op een dual core systeem).

If it ain't broken it doesn't have enough features


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Apache schreef op zondag 04 maart 2007 @ 03:24:
Ik zeg misschien iets doms maar, die setters worden toch slechts opgeroepen bij het tot leven brengen van de bean, wanneer de methodes van de bean zoals employeeManager.fire(int) worden aangeroepen is dit toch al lang achter de rug en heeft bean al lang de dao ter zijner beschikking?
In het geval van java 5 icm spring dan wel.

Maar als je werkt onder java 1.4 of als er geen happens before relatie tussen een variable write en een read is, dan heb je geen garantie dat thread1 de waarde geschreven in thread2 voor een variable a ook daadwerkelijk ziet. Jij denkt in termen van sequential consistency (lees het begin nog even na van mijn blogpost) en java ondersteunt dit dus helemaal niet.
Pagina: 1