annotate rdfdb/service.py @ 130:d195a5f50137

rename isEmpty to match js
author drewp@bigasterisk.com
date Mon, 29 May 2023 16:14:17 -0700
parents a71e4272d808
children 8fa6a47521d7
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
1 import functools
80
22c9679dbf67 reformat
drewp@bigasterisk.com
parents: 72
diff changeset
2 import itertools
22c9679dbf67 reformat
drewp@bigasterisk.com
parents: 72
diff changeset
3 import logging
89
1120c6489888 refactor, redo the SyncedGraph class ordering, fix most typing errors
drewp@bigasterisk.com
parents: 87
diff changeset
4 from pathlib import Path
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
5 from typing import Dict, Optional, cast
46
3b36b2c8ae65 fix bug that echoed a patch back to KC. https://bigasterisk.com/light9/work/2019/kc-patch-echo-bug.png
Drew Perttula <drewp@bigasterisk.com>
parents: 45
diff changeset
6
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
7 from rdflib import URIRef
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
8 from starlette.applications import Starlette
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
9 from starlette.endpoints import WebSocketEndpoint
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
10 from starlette.requests import Request
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
11 from starlette.responses import PlainTextResponse, Response
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
12 from starlette.routing import Route, WebSocketRoute
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
13 from starlette.websockets import WebSocket
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
14 from starlette_exporter import PrometheusMiddleware, handle_metrics
13
c9d1764d64ad add web server. remove more traces of light9
Drew Perttula <drewp@bigasterisk.com>
parents:
diff changeset
15
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
16 from rdfdb.file_vs_uri import DirUriMap
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
17 from rdfdb.patch import Patch
107
19100db34354 s/Db/SharedGraph/
drewp@bigasterisk.com
parents: 105
diff changeset
18 from rdfdb.shared_graph import SharedGraph
52
c81bd512d587 start the stats/ page
drewp@bigasterisk.com
parents: 48
diff changeset
19
120
3733efe1fd19 finesse logging
drewp@bigasterisk.com
parents: 109
diff changeset
20 log = logging.getLogger('rdfdb.net')
13
c9d1764d64ad add web server. remove more traces of light9
Drew Perttula <drewp@bigasterisk.com>
parents:
diff changeset
21
65
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
22 _wsClientSerial = itertools.count(0)
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
23
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
24
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
25 class SyncedGraphSocket(WebSocketEndpoint):
65
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
26 """
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
27 Send patches to the client (starting with a client who has 0
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
28 statements) to keep it in sync with the graph.
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
29
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
30 Accept patches from the client, and assume that the client has
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
31 already applied them to its local graph.
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
32
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
33 Treat a disconnect as 'out of sync'. Either the client thinks it
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
34 is out of sync and wants to start over, or we can't apply a patch
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
35 correctly therefore we disconnect to make the client start over.
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
36
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
37 This socket may also carry some special messages meant for the
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
38 rdfdb web UI, e.g. about who is connected, etc.
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
39 """
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
40 encoding = "text"
65
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
41
107
19100db34354 s/Db/SharedGraph/
drewp@bigasterisk.com
parents: 105
diff changeset
42 def __init__(self, db: SharedGraph, *a, **kw):
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
43 WebSocketEndpoint.__init__(self, *a, **kw)
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
44 self.db = db
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
45 self.connectionId = f'WS{next(_wsClientSerial)}' # unneeded?
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
46 log.info(f'ws connection init {self.connectionId}')
65
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
47
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
48 def __repr__(self):
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
49 return f'<WebSocket {self.connectionId}>'
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
50
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
51 async def on_connect(self, websocket: WebSocket):
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
52 await websocket.accept()
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
53 await websocket.send_json({'connectedAs': self.connectionId})
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
54 log.info(f"new ws client {self.connectionId}")
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
55
109
bc643d61bb7c format & comments
drewp@bigasterisk.com
parents: 107
diff changeset
56 await self.db.addClient(self.connectionId, functools.partial(self._onPatch, websocket))
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
57
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
58 async def _onPatch(self, websocket: WebSocket, p: Patch):
126
1780382477ed turn a disconnect into a warning
drewp@bigasterisk.com
parents: 123
diff changeset
59 try:
1780382477ed turn a disconnect into a warning
drewp@bigasterisk.com
parents: 123
diff changeset
60 await websocket.send_text(p.makeJsonRepr())
1780382477ed turn a disconnect into a warning
drewp@bigasterisk.com
parents: 123
diff changeset
61 except RuntimeError:
1780382477ed turn a disconnect into a warning
drewp@bigasterisk.com
parents: 123
diff changeset
62 # likely WS disconnect
1780382477ed turn a disconnect into a warning
drewp@bigasterisk.com
parents: 123
diff changeset
63 log.warning("onPatch failed- hope the client calls back")
65
9bc9de580033 big rewrite of rdfdb and syncedgraph to use a single websocket connecton
Drew Perttula <drewp@bigasterisk.com>
parents: 59
diff changeset
64
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
65 async def on_receive(self, websocket: WebSocket, data: str):
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
66 if data == 'PING':
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
67 await websocket.send_text('PONG')
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
68 return
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
69 log.debug("%r sends patch to us: %s", self, data[:64])
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
70 p = Patch(jsonRepr=data)
127
a71e4272d808 cut way down on some no-op patches that were causing file rewrites/rereads
drewp@bigasterisk.com
parents: 126
diff changeset
71
a71e4272d808 cut way down on some no-op patches that were causing file rewrites/rereads
drewp@bigasterisk.com
parents: 126
diff changeset
72 # this is very important- I caught clients piling up dozens of retries, causing
a71e4272d808 cut way down on some no-op patches that were causing file rewrites/rereads
drewp@bigasterisk.com
parents: 126
diff changeset
73 # us to rewrite files then notice them, etc. Problem may not be fully solved.
130
d195a5f50137 rename isEmpty to match js
drewp@bigasterisk.com
parents: 127
diff changeset
74 if p.isEmpty():
127
a71e4272d808 cut way down on some no-op patches that were causing file rewrites/rereads
drewp@bigasterisk.com
parents: 126
diff changeset
75 return
a71e4272d808 cut way down on some no-op patches that were causing file rewrites/rereads
drewp@bigasterisk.com
parents: 126
diff changeset
76
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
77 try:
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
78 await self.db.patch(p, sender=self.connectionId)
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
79 except ValueError as e:
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
80 log.warning(f'patch from {self!r} did not apply: {e!r}')
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
81 # here we should disconnect that client and make them reset
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
82 await websocket.close()
80
22c9679dbf67 reformat
drewp@bigasterisk.com
parents: 72
diff changeset
83
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
84 async def on_disconnect(self, websocket, close_code):
123
4f8e1a447b12 logging
drewp@bigasterisk.com
parents: 120
diff changeset
85 log.info("bye ws client %r: close_code=%s", self.connectionId, close_code)
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
86 self.db.clientDisconnected(self.connectionId)
13
c9d1764d64ad add web server. remove more traces of light9
Drew Perttula <drewp@bigasterisk.com>
parents:
diff changeset
87
c9d1764d64ad add web server. remove more traces of light9
Drew Perttula <drewp@bigasterisk.com>
parents:
diff changeset
88
107
19100db34354 s/Db/SharedGraph/
drewp@bigasterisk.com
parents: 105
diff changeset
89 def get_graph(db: SharedGraph, request: Request) -> Response:
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
90 accept = request.headers.get('accept', '')
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
91 if accept == 'text/plain':
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
92 format = 'nt'
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
93 elif accept == 'application/n-quads':
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
94 format = 'nquads'
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
95 else:
13
c9d1764d64ad add web server. remove more traces of light9
Drew Perttula <drewp@bigasterisk.com>
parents:
diff changeset
96 format = 'n3'
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
97 return PlainTextResponse(db.graph.serialize(format=format))
13
c9d1764d64ad add web server. remove more traces of light9
Drew Perttula <drewp@bigasterisk.com>
parents:
diff changeset
98
45
dc61012eeace python reformat
Drew Perttula <drewp@bigasterisk.com>
parents: 43
diff changeset
99
107
19100db34354 s/Db/SharedGraph/
drewp@bigasterisk.com
parents: 105
diff changeset
100 async def post_prefixes(db: SharedGraph, request: Request) -> Response:
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
101 suggestion = await request.json()
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
102 db.addPrefixes(
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
103 ctx=URIRef(suggestion['ctx']), #
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
104 prefixes=dict((cast(str, k), URIRef(v)) for k, v in suggestion['prefixes'].items()))
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
105 return Response(status_code=204)
45
dc61012eeace python reformat
Drew Perttula <drewp@bigasterisk.com>
parents: 43
diff changeset
106
dc61012eeace python reformat
Drew Perttula <drewp@bigasterisk.com>
parents: 43
diff changeset
107
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
108 def makeApp(dirUriMap: Optional[DirUriMap] = None, prefixes: Optional[Dict[str, URIRef]] = None) -> Starlette:
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
109 log.info('makeApp start')
13
c9d1764d64ad add web server. remove more traces of light9
Drew Perttula <drewp@bigasterisk.com>
parents:
diff changeset
110
c9d1764d64ad add web server. remove more traces of light9
Drew Perttula <drewp@bigasterisk.com>
parents:
diff changeset
111 if dirUriMap is None:
89
1120c6489888 refactor, redo the SyncedGraph class ordering, fix most typing errors
drewp@bigasterisk.com
parents: 87
diff changeset
112 dirUriMap = {Path('data/'): URIRef('http://example.com/data/')}
13
c9d1764d64ad add web server. remove more traces of light9
Drew Perttula <drewp@bigasterisk.com>
parents:
diff changeset
113 if prefixes is None:
c9d1764d64ad add web server. remove more traces of light9
Drew Perttula <drewp@bigasterisk.com>
parents:
diff changeset
114 prefixes = {
42
541010f99d61 type fixes
Drew Perttula <drewp@bigasterisk.com>
parents: 40
diff changeset
115 'rdf': URIRef('http://www.w3.org/1999/02/22-rdf-syntax-ns#'),
541010f99d61 type fixes
Drew Perttula <drewp@bigasterisk.com>
parents: 40
diff changeset
116 'rdfs': URIRef('http://www.w3.org/2000/01/rdf-schema#'),
541010f99d61 type fixes
Drew Perttula <drewp@bigasterisk.com>
parents: 40
diff changeset
117 'xsd': URIRef('http://www.w3.org/2001/XMLSchema#'),
13
c9d1764d64ad add web server. remove more traces of light9
Drew Perttula <drewp@bigasterisk.com>
parents:
diff changeset
118 }
45
dc61012eeace python reformat
Drew Perttula <drewp@bigasterisk.com>
parents: 43
diff changeset
119
107
19100db34354 s/Db/SharedGraph/
drewp@bigasterisk.com
parents: 105
diff changeset
120 db = SharedGraph(dirUriMap=dirUriMap, prefixes=prefixes)
13
c9d1764d64ad add web server. remove more traces of light9
Drew Perttula <drewp@bigasterisk.com>
parents:
diff changeset
121
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
122 app = Starlette(
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
123 debug=True,
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
124 routes=[
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
125 Route('/graph', functools.partial(get_graph, db)),
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
126 WebSocketRoute('/syncedGraph', functools.partial(SyncedGraphSocket, db)),
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
127 Route('/prefixes', functools.partial(post_prefixes, db)),
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
128 ],
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
129 )
86
5b6e90a708ce some weak file_vs_uri coverage
drewp@bigasterisk.com
parents: 81
diff changeset
130
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
131 app.add_middleware(PrometheusMiddleware)
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
132 app.add_route("/metrics", handle_metrics)
86
5b6e90a708ce some weak file_vs_uri coverage
drewp@bigasterisk.com
parents: 81
diff changeset
133
105
4681ea3fcdf6 port to starlette asyncio
drewp@bigasterisk.com
parents: 104
diff changeset
134 return app