thought to share about hosting jsreport in azure app service and questions about async rendering



  • I'm working on a project that needs to:
    move on-prem jsreport instance to azure app service
    add template change tracking, in my repository devs can run jsreport that using /[myTempalteStore] as template-store locally

    I chose git for template change tracking and here are the issues I got so far and what I did to resolve / found workaround. hope it helps :D

    1. Jsreport docker instance did not respond to warm-up call,
      This happens when you scale-out. Every new instance will end up in this.
      Restart the instance will work but I don't like restarting each new instance during scale-out
      The root cause is when the app service tries to run the docker instance it tries to listen to port 80, ignoring the docker file's specification of 5488
      You configure appservice to listen to 5488 instead, using config WEBSITES_PORT

    2. template-store issues:
      since it's scaled I don't want call api for template deployment for each instance, i need a shared source of truth.

    2.1 I first tried azure file share as template store according to the instruction from jsreport home page.
    template deployment is done by
    -> 2.1.1 azcopying the files from git to azure file share when a commit is pushed
    -> 2.2.2 then restart staging-slot
    -> 2.3.3 then swap

    I don't need 2.1.2 and 2.1.3 if I turn on the external change sync on jsreport but looks like it's not recommended

    It ends up in too many transactions and slow template deployment/startup since azure file share has a 60mb/s limit on it's default tier

    2.2 then I tried git push to app service's local git
    app service's local git is at /home/site/repository, by default when you push it sync to /home/site/wwwroot and restart all instances. Looks like this is not restart all but a restart one by one based on my observation, so in theory I don't need deployment slots and deployment can give me 0 downtime.

    having WEBSITES_ENABLE_APP_SERVICE_STORAGE turned on makes /home folder shared to all jsreport instances
    I made all jsreport to use the /home/site/wwwroot/[myTemplateFolder] as the template store.

    now I don't have that 60mb/s issue and I don't need to worry about the per-transaction cost in azure file share

    but when I scale-out or git push, some instance randomly dies. After looking at the log it shows there's a concurrency issue during startup on file fs.lock

    Q: I'm wondering if jsreport can have an option that just take a folder as read-only template-store without creating additional files and monitoring over that folder

    to resolve this I have to make every instance having it's own local template-store
    each instance has it's own /tmp folder and it's cleared during restart.
    so I was adding the startup command:
    copy /home/site/wwwroot/[myTemplateFolder] to /tmp/[myTemplateFolder]
    chown for jsreport to that folder

    I believe this was to run at instance level, before jsreport start.
    But it ends up in all instance won't start
    After a bit research I found azure app service will overwrite docker file's CMD / ENTRYPOINT
    so adding "sh run.sh" (equivalent to CMD["sh","run.sh"], this is the last step in jsreport's docker file)

    now I've got the scale-ability and 0-downtime during deployment

    1. Async rendering
      some of our system can utilize the async rendering from jsreport but I found it's lack of the [callback] feature so I decided to do this myself.

    with blob-storage enabled for jsreport, I can have an azure function that is triggered when a blob file is created.
    however the blob file name is random so I have to inspect the response of the async rendering to get the report name from url and map the random name with my callback so azure function knows what to do

    Q: I'm thinking if it's possible to have a callback mechanism from jsreport so I don't need to keep the mapping from the random name to my callback

    In addition I found a hidden param for the reports extension that blow api call can give me the exact filename, then my azure function can just do blob name-pattern to extract the callback parameters

    Q: is it safe to use below request? I found that from jsreport repository but it's not mentioned in doco
    {
    "template": {
    "name": "MyInvoiceTemplate"
    },
    "options": {"reports": {"async": true, "blobName": "invoice112345"}}
    }



  • Thank you for sharing this. As soon as I have some time I will update the documentation.

    but when I scale-out or git push, some instance randomly dies. After looking at the log it shows there's a concurrency issue during startup on file fs.lock

    That is likely because of slow file system.
    The error comes from the lock library we use to make sure we don't access the file system in parallel.
    https://github.com/npm/lockfile
    The same error is described also here
    https://github.com/npm/lockfile/issues/26

    The following jsreport configs with defaults are used as options for the locking library.

    extensions_fsStore_persistence_lock_stale=5000
    extensions_fsStore_persistence_lock_retries=100
    extensions_fsStore_persistence_lock_retryWait=100
    

    You can see we use quite many retries to allow long operations. It looks like the file share has really bad latency in your case. You may still try to increase the retires or change stale to see if it helps.

    Q: I'm wondering if jsreport can have an option that just take a folder as read-only template-store without creating additional files and monitoring over that folder

    I'm afraid this isn't supported. The common solution as you observed is to map the data to some tmp or tmpfs

    Q: I'm thinking if it's possible to have a callback mechanism from jsreport so I don't need to keep the mapping from the random name to my callback

    The callback isn't implemented in the reports extension. I've added it to the backlog.
    https://github.com/jsreport/jsreport/issues/1091
    If you have any suggestions about how the callback request should look like and how it should be configured, please add it to github issue.

    I've seen somewhere here a guy solving this using jsreport script and after render hook.
    https://jsreport.net/learn/scripts

    Q: is it safe to use below request? I found that from jsreport repository but it's not mentioned in doco

    Yes.


Log in to reply
 

Looks like your connection to jsreport forum was lost, please wait while we try to reconnect.