Home General
New Blog Posts: Merging Reports - Part 1 and Part 2

RB threads and NexusDB database errors (v 15.05)

edited February 2015 in General
Hello

Our customers get occasional "System has been illegally re-entered"
errors when running reports, and we know that this is ONLY caused when
different threads are accessing the same session concurrently. We can
see in the error log that TppPageRequestThread is involved (we WANT its
functionality) and that its ExecuteReport method has a comment thus:

// enable background generation, without requiring a separate
database connection
ppUtils.ppBeginBackgroundPrinting;

Digging deeper we see that ppBeginBackgroundPrinting just increments the
global var "uBackgroundPrinting". Fine.

But it seems to us that the crucial test function "ppInMainThread" is
wrong (or at least it doesn't make sense to us):

if (uOutlinePreviewing = 0) and (uBackgroundPrinting = 0) then
Result := GetCurrentThreadID = MainThreadID
else
Result := True;

end; {function, ppInMainThread}

Surely if TppPageRequestThread has incremented uBackgroundPrinting then
the ppInMainThread will always return TRUE, which isn't necessary
correct is it? Why not just ..

Result := GetCurrentThreadID = MainThreadID

...?

Given the prevalence of ppInMainThread being called throughout the RD
code, could this be a problem? Or have we misunderstood something
fundamental.

(Apologies for the invidious challenging of your code - we are just at a
loss to find any solution for this and its causing us headaches).

Comments

  • edited February 2015
    Hi Paul,

    ReportBuilder does not spawn multiple threads that simultaneously access
    a DB session. It uses a single background thread that solely accesses
    the database.

    If you are having issues multiple threads accessing the same session, it
    is likely due to visual controls (or other objects) sharing the same DB
    connections as ReportBuilder. The best option is to keep all Report
    datasets and connections separate from other DB controls and objects in
    your application. You can also try disabling them before printing.

    Best Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com
  • edited February 2015
    Hello Nico,

    We deliberately give RB is own session and database to avoid any
    conflict with any other part of our application. You say that RB doesn't
    does not spawn multiple threads that simultaneously access
    a DB session, but surely that's ONLY the case if the "ppInMainThread"
    function is returning correctly (isn't that the point of this function?)
    and that is what I am questioning. It doesn't look right to me, but
    maybe I have a blind spot here. Could you confirm?

    P.S. I emailed you accidentally instead of commenting. Always get that
    wrong when using Thunderbird. Sorry.


  • edited February 2015
    Hi Paul,

    The code you are referring to is used in numerous placed in
    ReportBuilder including the Server and Background Printing. It should
    not be altered.

    When using the multi-page view (threaded page cache) only a single
    thread is created to handle page generation in the background. This
    thread accesses the database. ReportBuilder does not employ the main
    thread to access any data during this time so if Nexus is detecting a
    conflict, it is likely elsewhere in your application.

    If you have a simple example that reliably recreates this issue using
    only Delphi, RB, and Nexus components, please send it to us in .zip
    format and we will test it with the latest version of Nexus.

    Best Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com
  • edited February 2015
    Thanks for the reply, Nico.

    I think I've worked something out. It only happens when switching from
    from the Preview page of the Report Designer back to the Design tab.
    Now, this forces the TppTemplate.OnLoadEnd event to fire and we use that
    when loading the report from database (Report.Template.OnLoadEnd :=
    OnTemplateLoadEnd) and indeed we DO access the database from the session
    the report uses to read in some meta-data that we store in the reporting
    table.

    I think that what's happening is the cancelling of the report is
    happening asynchronously, so the thread that is running the report is
    occasionally still tidying up when OnLoadEnd gets called. Now, if there
    was a way of telling that the OnLoadEnd event is being called in the
    context of the report being restored (via
    TppDesignPreviewManager.RestoreReportFromStream) rather than loaded from
    database then we could jump out of OnLoadEnd and I think we'll be safe.
    Any suggestions?



  • edited February 2015
    Or simply use :

    procedure TnxRBDesignerEngine.DesignerTabChange(Sender: TObject; NewTab:
    string; var AllowChange: Boolean);
    begin
    if NewTab = 'Preview' then
    Report.Template.OnLoadEnd := nil else
    Report.Template.OnLoadEnd := OnTemplateLoadEnd;
    end;


    :) Thanks for helping me end up here.


This discussion has been closed.