The story evolved down here.
I have crafted an extra observer that listens to notifications and checks if the values
are the same when in event and one second later in a spawned thread. They're not.
The code is copied below, it is inspired from the nice tutorial about observation.
Since about 36h, the observation listener below has reported 967 saves. From them, 145
indicate the warning at the end of this code.
This warning indicates that the title, number of a objects of a particular class (where we
observed the bug first), or version differ:
- when pulled from the source of the event (XWikiDocument or LazyXWikiDocument)
- or when pulled one second later with xwiki.getXWiki().getDocument().
However, and that's the crusty bit.
Somehow, the attempt to reproduce the bug have almost all failed since that time as well.
(no systematic way to reproduce the bug has been found, neither has there been a page that
stayed different between the two nodes). We're on hold but this is a rather good
surprise!
Paul
It was the
request of previous versions which has failed.
All that seems quite curriki specific in there is a simple listener, using the
old-style-notification, which analyzes the document and indexes it and maybe
"related" documents (for example reindexes the user-document so as to display
the count of contributions, or reindexes all of the resources of the user if the user has
changed his name...). The IndexUpdater of the LucenePlugin does not seem to care for this
(but then, it doesn't seem to measure differences).
OK will try with 50 and see
what I get. Not sure how this could be
related unless it makes the LazyXWikiDocument fail to get the document
from the database for some weird reason.
// {{groovy}}
// This page should be Util.TryObservation
//
import org.xwiki.observation.*
import org.xwiki.observation.event.*
import com.xpn.xwiki.web.*
import com.xpn.xwiki.*
import org.xwiki.bridge.event.DocumentUpdatedEvent
import com.xpn.xwiki.doc.XWikiDocument
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import com.xpn.xwiki.util.AbstractXWikiRunnable
import org.xwiki.observation.remote.RemoteObservationManagerContext
class TryObservation implements EventListener
{
static final Log LOG =
LogFactory.getLog("groovyPages.Sandbox.TryObservation");
boolean DEBUG = LOG.isDebugEnabled();
XWiki xwiki
XWikiContext xcontext
TryObservation(xwiki, xcontext)
{
this.xwiki = xwiki
this.xcontext = xcontext
}
String getName()
{
// The unique name of this event listener
return "Changes-Version-Checker"
}
List<Event> getEvents()
{
// The list of events this listener listens to
return Arrays.asList(new DocumentUpdatedEvent())
}
// Called by the Observation Manager when an event matches the list of events
returned
// by getEvents()
void onEvent(Event event, Object source, Object data)
{
XWikiDocument xdoc = source;
LOG.warn("Received event: " + event + ", source: " + source);
LOG.warn("Title of changed document " + xdoc.getTitle())
LOG.warn("Version of changed document " + xdoc.getVersion())
int count = 0;
List o = xdoc.getObjects("CurrikiCode.SubAssetClass");
if(o!=null) count = o.size()
LOG.warn("Number of children: " + count);
LOG.warn("Is remote? " +
Utils.getComponent(RemoteObservationManagerContext.class).isRemoteState());
Thread obs = new Thread(new DelayedObserver(xwiki, xcontext, xdoc.getFullName(),
xdoc.getTitle(), xdoc.getVersion(), count),"delayed obs");
obs.start();
LOG.warn("started obs " + obs);
// Prevent infinite recursion since in this example we log to wiki page which
// triggers a document change... :)
/* if (source.fullName != "Sandbox.TryObservation") {
def document = xwiki.getDocument("Sandbox.TryObservation")
document.setContent("${document.getContent()}\n${source.fullName} has
changed")
document.save("Logging event", true)
} */
}
public static class DelayedObserver extends AbstractXWikiRunnable {
public DelayedObserver(XWiki xwiki, XWikiContext context, String fullName, String
inEventTitle, String inEventVersion, int inEventCount) {
super(XWikiContext.EXECUTIONCONTEXT_KEY, context.clone());
LOG.warn("(delayed) delayed observer initting. ")
this.xwiki = xwiki;
this.fullName = fullName;
this.context = context;
this.inEventTitle = inEventTitle;
this.inEventVersion = inEventVersion;
this.inEventCount = inEventCount;
}
private XWiki xwiki;
private XWikiContext context;
private String fullName, inEventTitle, inEventVersion;
private int inEventCount;
public void runInternal() {
try {
Thread.currentThread().setName("delayed observer " + fullName);
LOG.warn("(delayed) delayed observer starting. ")
Thread.sleep(1000);
XWikiDocument doc = xwiki.getDocument(fullName, context);
LOG.warn("(delayed) Title of changed document " +
doc.getTitle())
LOG.warn("(delayed) Version of changed document " +
doc.getVersion())
int count = 0;
List o = doc.getObjects("CurrikiCode.SubAssetClass");
if(o!=null) count = o.size()
LOG.warn("(delayed) Number of children: " + count);
LOG.warn("(delayed) Is remote? " +
Utils.getComponent(RemoteObservationManagerContext.class).isRemoteState());
if(!(inEventVersion==doc.getVersion() &&
inEventTitle==doc.getTitle() && count == inEventCount)) {
LOG.error("(delayed) WARNING: inEvent and and delayed do not
match!!");
}
} catch (Exception ex) {
LOG.warn("Exception at the delayed bit: "+ ex, ex);
}
}
}
}
// Register against the Observation Manager
def observation = Utils.getComponent(ObservationManager.class)
observation.removeListener("Changes-Version-Checker")
def listener = new TryObservation(xwiki.getXWiki(), xcontext.getContext())
observation.addListener(listener)
// {{/groovy}}