Michiel Meeuwissen wrote:
Nico Klasens wrote:
1 User layer - thiu layer is user-centric and every action is based on
this user, his role and the security constraints. You could say this is
the bridge, because the cloud has a user and it applies the securtiy
constriants
2 Repository layer. This layer is used by all users and exposes all that
is in the repository. This layer maintains the model: builders,
typedefs, reldefs typerels oaliases, etc. Data which is in this layer
can be seen by all users. Data in the User layer can be specific for
that user (local modification).
3 Storage layer. This layer is responsible for pesistence and retrieval
of data. The repository layer uses this to store and retrieve nodes.
This layer decides how and when to interact with the persistent store
(database). It could use a cache instead of the trip to the persistent
store.
At the moment, the processors are called in the bridge which is
user-centric. A procesor should work just for one user and one request.
The loop example is executed for in one request and could be a local
modification until it is saved to the repository layer (this is what a
transaction does).
What my processors actually did was simply having a small static storage in
memory for those fields, and simply translate every change in 'don't change
it', and checks it on every get. One can find that an odd location, but it
is rather pragmatic, because it is shallow, and easy to implement.
iow, a quick fix :)
As a perfomance fix I would make sure the application spends as little
on the poll click. For a mmbase poll this means never cache poll data.
The queries (select and update) are fast enough for the database to
handle. You just have to configure everything else to stay in cache :)
I think that whay you say can actually be interpreted as that the current
loop in DatabaseStorageManager#change(MMObjectNode, MMObjectBuilder), to
check if there are changed fields, is wrong in the first place, and the
'change' method should not have been called at all then, and that you could
more easily agree if I moved the check from there to MMObjectNode itself to
make getChanged ignore these kind of bogus-changes. Correct?
MMObject.setValue(String fieldName, Object fieldValue) altready has such
a check. I don't know if this still works correctly.
IMO it is a quick fix, because it solves somthing in the wrong position
of the system When it is really desired to delay the database call then
the storage layer should make this decision. The processors are just to
high on the chain to be able to make a good decision. With MMbase we try
to reduce the load on the database, because mmbase can easily create a
starvation on database resources. But I don't think this is the database
killing example. It is more the many small select statements which use a
new db connection which kill the database. This update example is just
being nice for mmbase itself (caches and events).
We cache db-connections, don't we?
Yes we have a connection pool, but have you every looked how it is used?
A StorageManager is a wrapper around a connection. For every select we
request a new StorageManager from the factory which means get a new
connection from the pool. Add this to the log4j.xml and see how many
select queries MMBase creates.
<logger name="org.mmbase.storage.implementation.database"
additivity="false">
<level class="&mmlevel;" value ="debug" />
<appender-ref ref="logfile" />
</logger>
It is probably more then you would like I have seen many mmbase
applications in production doing 500+ queries to get one page.
This obvious puts some heavy work on the cache and many queries are
dropped with concurrent users. After some days of tuning, it might be
possible to run only on caches. Many people don't realize which queries
are done when you use the taglib or bridge.
Just an example I came across this week in an article template
--------------------------------------------
<mm:node number="${elementId}" notfound="skip">
<mm:field name="title" />
<mm:field name="body" escape="none"/>
<mm:countrelations role="posrel" searchdir="destination"
id="secondaryContentCount" write="false" />
<c:if test="${secondaryContentCount gt 0}">
<mm:relatednodes type="article" role="posrel"
orderby="posrel.pos" searchdir="destination">
</mm:relatednodes>
<mm:relatednodes type="gallery" role="posrel"
orderby="posrel.pos" searchdir="destination">
</mm:relatednodes>
<mm:relatednodes type="faqitem" role="posrel"
orderby="posrel.pos" searchdir="destination">
</mm:relatednodes>
<mm:relatednodes type="newsitem" role="posrel"
orderby="posrel.pos" searchdir="destination">
</mm:relatednodes>
<mm:relatednodes type="page" role="posrel" orderby="posrel.pos"
searchdir="destination">
</mm:relatednodes>
<mm:relatednodes type="attachments" role="posrel"
orderby="posrel.pos" searchdir="destination">
</mm:relatednodes>
<mm:relatednodes type="urls" role="posrel" orderby="posrel.pos"
searchdir="destination">
</mm:relatednodes>
</c:if>
</mm:node>
--------------------------------------------
In many cases the article did not have secondary content. Without the
"if" the template executes 7 queries which results in many cases with
an empty list. All 7 queries are stored in the cache and push other
queries out of the cache which might have been more relevant. Of course
the "if" is a quick fix, but it will result in better usage of the cache.
I am convinced that you don't need a delayed update fix when you
optimize your application correctly.
Nico
_______________________________________________
Developers mailing list
[email protected]
http://lists.mmbase.org/mailman/listinfo/developers