justpy icon indicating copy to clipboard operation
justpy copied to clipboard

high network traffic with periodic requests

Open eudoxos opened this issue 4 years ago • 4 comments

Hi, JustPy is great, though now for the first time I run an app over a DSL line of less than stellar quality. The page is running periodic update() with 0.5 sleep in-between. Most the time, nothing in the page changes at all, but it is transferring 2MB/s continuously. The page itself is not complicated, only contains a large image with about 1MB worth of inline data (img=jp.Img(...), later img.src='data:image/jpeg;base64,'+base64.b64encode(...).decode('ascii')); the image almost never changes.

I am suspecting the image data are transferred with every update(). Am I correct? Is there a way to prevent that? There is a mention of use_cache but it is too terse for me to understand whether it is something to be used.

Could I just call update() method of only the components that actually changed, or is calling update() of the whole page necessary?

If what I write above does not make much sense, I will try to prepare a minimal example. If it does make sense, I will be happy for pointers how to decrease the traffic.

eudoxos avatar Dec 23 '20 19:12 eudoxos

It makes sense, I understand what you mean. Yes, the page is transferred every update.

  1. use_cache will not help you here because it does not stop the page being sent, only being rendered (meaning creating a JSON object from the JustPy representation) in the back end.
  2. You can update just the component you need. Please see the following example: https://justpy.io/tutorial/pushing_data/#simple-message-board
  3. I am not 100% this will work, but you could consider making the image a static file and getting it loaded via the src. So instead of having an inline image, have the src point to a static resource. I think then the browser will cache the image for you. The update size would be smaller also. You could rewrite the image file when it changes to your static directory. https://justpy.io/tutorial/static/

I hope this helps, please let me know what worked best for you.

elimintz avatar Dec 24 '20 04:12 elimintz

Alright, what works the best is 3 but modified with counter:

@jp.SetRoute('/current_image/{number}')
def get_image():
    return starlette.responses.Respose(content=jpgData,status_code=200,media_type='image/jpeg')

and the image is change by setting img.src='/current_image/%d'%counter.

I also experimented without counter and reurning status_code=304 when the image did not change since the last request, but the browser would not try to fetch the image at every page refresh.

As to your point 2 (partial udpate). This MWE should put button to the "loading" state (with spinner) for 2 seconds of the callback, and the back again. It does not happen unless the whole page is updated. Am I misunderstanding something?

import justpy as jp
import time

@jp.SetRoute('/')
def main():
    wp=jp.QuasarPage(dark=True)
    butt1=jp.QButton(text='busy with button-only update',a=wp,color='red')
    butt2=jp.QButton(text='busy with page update',a=wp,color='blue')
    async def busy1(b,msg=None):
        butt1.loading=True
        await butt1.update()
        time.sleep(2)
        butt1.loading=False
        await butt1.update()
        return True
    async def busy2(b,msg=None):
        butt2.loading=True
        await wp.update()
        time.sleep(2)
        butt2.loading=False
        await wp.update()
        return True
    butt1.on('click',busy1)
    butt2.on('click',busy2)
    return wp

jp.justpy(host='0.0.0.0')

eudoxos avatar Dec 24 '20 07:12 eudoxos

Yes, definitely a bug. You need to add butt1.add_page(wp) after creating butt1 as components do not know which pages they are on, but even when I add that, this example does not work. I am working on this.

elimintz avatar Dec 29 '20 18:12 elimintz

Strange bug. For some reason create_task does not work anymore. Change the object's update to the following and it works:

    async def update(self, socket=None):
        component_dict = self.convert_object_to_dict()
        if socket:
            # WebPage.loop.create_task(socket.send_json({'type': 'component_update', 'data': component_dict}))
            await socket.send_json({'type': 'component_update', 'data': component_dict})
        else:
            pages_to_update = list(self.pages.values())
            print(pages_to_update)
            for page in pages_to_update:
                try:
                    websocket_dict = WebPage.sockets[page.page_id]
                except:
                    continue
                for websocket in list(websocket_dict.values()):
                    try:
                        # WebPage.loop.create_task(websocket.send_json({'type': 'component_update', 'data': component_dict}))
                        await websocket.send_json({'type': 'component_update', 'data': component_dict})
                    except:
                        print('Problem with websocket in component update, ignoring')
        return self

And don't forget to add butt1.add_page(wp). It will work now. The changes to update are the lines commented out and the line added is just below them. I don't understand yet what stopped working.

elimintz avatar Dec 29 '20 19:12 elimintz

see #685

WolfgangFahl avatar Sep 20 '23 09:09 WolfgangFahl