Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

removing rows on backend table does not replicate to client table #2293

Open
cakir-enes opened this issue Jul 5, 2023 · 3 comments
Open
Labels
bug Concrete, reproducible bugs needs-repro Needs a reproduction or example of the issue

Comments

@cakir-enes
Copy link

cakir-enes commented Jul 5, 2023

Not sure if this is the expected behavior, but following this guide https://perspective.finos.org/docs/server/#clientserver-replicated the replica table on client, which is derived by a view that backed by the backend table does not propagate removes.

Is this the expected behavior?

version 2.3.1, backend : python fastapi

@dariosky
Copy link

dariosky commented Aug 9, 2023

Hi @cakir-enes - can you provide an example of the code where you're trying to remove records, so we can help you?

@timkpaine timkpaine added bug Concrete, reproducible bugs needs-repro Needs a reproduction or example of the issue labels Aug 14, 2023
@nickroci
Copy link
Contributor

+1 for this, when I noticed it ages ago it could be repoed by creating a table in python, connect web viewer, asyncio.sleep for 30 seconds table.remove([key]) in python. Observe that they key still exists in Web viewer. I'll see if I can get something specific next week.

It seemed to me that the ws protocol couldn't really deal with deletes.

Fyi a workaround is to have a second ws and manually call remove in javascript on the table...

@nickroci
Copy link
Contributor

nickroci commented Oct 4, 2023

As promised too long ago, notice that data is updated and added OK and the ws is sending something on delete.

The workaround is to add a second ws and just send messages to your own js code to call delete in javascript...

As a side note this might make a good example for new developers as its quite hard to get started on the whole thing otherwise...

# pip install 'uvicorn[standard] fastapi perspective-python pandas
import asyncio
import logging
import threading
import uvicorn

from fastapi import FastAPI, WebSocket
from fastapi.middleware.cors import CORSMiddleware
from starlette.responses import HTMLResponse

import pandas as pd

from perspective import Table, PerspectiveManager, PerspectiveStarletteHandler

def perspective_thread(manager):
    """Perspective application thread starts its own event loop, and
    adds the table with the name "data_source_one", which will be used
    in the front-end."""
    psp_loop = asyncio.new_event_loop()
    manager.set_loop_callback(psp_loop.call_soon_threadsafe)
    data = pd.DataFrame({
        "index": list(range(100)),
        "float": [i * 1.5 for i in range(100)],
        "bool": [True for i in range(100)],
        "string": [str(i) for i in range(100)]
    })
    table = Table(data, index="index")
    manager.host_table("data_source", table)
    async def deleteme():
        for i in range(100):
            print("DELETEING", i)
            table.remove([i])
            print(table.num_rows())
            print("Adding Junk")
            table.update({'index': [i+100], 'float': [1], "bool": [False], 'string': ['newData?']})
            table.update({'index': [i+99], 'float': [1], "bool": [False], 'string': ['Modified']})
            await asyncio.sleep(10)
    psp_loop.run_until_complete(deleteme())


def make_app():
    manager = PerspectiveManager()

    thread = threading.Thread(target=perspective_thread, args=(manager,))
    thread.daemon = True
    thread.start()

    async def websocket_handler(websocket: WebSocket):
        handler = PerspectiveStarletteHandler(manager=manager, websocket=websocket)
        await handler.run()

    app = FastAPI()
    app.add_api_websocket_route("/websocket", websocket_handler)
    
    @app.get("/", response_class=HTMLResponse)
    async def read_items():
        html_content = """
        <!DOCTYPE html>
        <html>
        <head>
<script type="module"  src="https://cdn.jsdelivr.net/npm/@finos/perspective/dist/cdn/perspective.js"></script>
<script type="module"  src="https://cdn.jsdelivr.net/npm/@finos/perspective-viewer/dist/cdn/perspective-viewer.js"></script>
<script type="module"  src="https://cdn.jsdelivr.net/npm/@finos/perspective-viewer-datagrid/dist/cdn/perspective-viewer-datagrid.js"></script>
<script type="module"  src="https://cdn.jsdelivr.net/npm/@finos/perspective-viewer-d3fc/dist/cdn/perspective-viewer-d3fc.js"></script>
<link rel="stylesheet" crossorigin="anonymous"  href="https://cdn.jsdelivr.net/npm/@finos/perspective-viewer/dist/css/pro.css"/>
            <script type="module">
            import perspective from "https://cdn.jsdelivr.net/npm/@finos/perspective/dist/cdn/perspective.js"
                    const websocket = perspective.websocket("ws://localhost:8080/websocket")
                    const worker = perspective.worker();
                    const server_table = await websocket.open_table("data_source");
                    const server_view = await server_table.view();
                    const table = await worker.table(server_view, {
                        index: await server_table.get_index()
                    });
                    document.getElementById('viewer').load(table);
            </script>
        </head>
        <body><perspective-viewer id="viewer" style="width: 500px; height: 500px"></perspective-viewer></body>
        </html>
        """
        return HTMLResponse(content=html_content, status_code=200)

    app.add_middleware(
        CORSMiddleware,
        allow_origins=["*"],
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )
    return app


if __name__ == "__main__":
    app = make_app()
    logging.critical("Listening on http://localhost:8080")
    uvicorn.run(app, host="0.0.0.0", port=8080)

@finos finos deleted a comment from sundeep1299 Oct 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Concrete, reproducible bugs needs-repro Needs a reproduction or example of the issue
Projects
None yet
Development

No branches or pull requests

4 participants