Skip to content

Commit 1b2d327

Browse files
authored
Durable user notifications and begin Cell embedding formalism (#2513)
1 parent 5adb01d commit 1b2d327

File tree

10 files changed

+465
-41
lines changed

10 files changed

+465
-41
lines changed

synapse/cortex.py

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
import synapse.lib.stormlib.project as s_stormlib_project # NOQA
6969
import synapse.lib.stormlib.version as s_stormlib_version # NOQA
7070
import synapse.lib.stormlib.modelext as s_stormlib_modelext # NOQA
71+
import synapse.lib.stormlib.notifications as s_stormlib_notifications # NOQA
7172

7273
logger = logging.getLogger(__name__)
7374
stormlogger = logging.getLogger('synapse.storm')
@@ -950,6 +951,28 @@ async def getAxonBytes(self, sha256):
950951
async for byts in self.cell.axon.get(s_common.uhex(sha256)):
951952
yield byts
952953

954+
@s_cell.adminapi()
955+
async def getUserNotif(self, indx):
956+
return await self.cell.getUserNotif(indx)
957+
958+
@s_cell.adminapi()
959+
async def delUserNotif(self, indx):
960+
return await self.cell.delUserNotif(indx)
961+
962+
@s_cell.adminapi()
963+
async def addUserNotif(self, useriden, mesgtype, mesgdata=None):
964+
return await self.cell.addUserNotif(useriden, mesgtype, mesgdata=mesgdata)
965+
966+
@s_cell.adminapi()
967+
async def iterUserNotifs(self, useriden, size=None):
968+
async for item in self.cell.iterUserNotifs(useriden, size=size):
969+
yield item
970+
971+
@s_cell.adminapi()
972+
async def watchAllUserNotifs(self, offs=None):
973+
async for item in self.cell.watchAllUserNotifs(offs=offs):
974+
yield item
975+
953976
class Cortex(s_cell.Cell): # type: ignore
954977
'''
955978
A Cortex implements the synapse hypergraph.
@@ -1151,7 +1174,6 @@ async def initServiceStorage(self):
11511174

11521175
# Initialize our storage and views
11531176
await self._initCoreAxon()
1154-
await self._initCoreJsonStor()
11551177

11561178
await self._initCoreLayers()
11571179
await self._initCoreViews()
@@ -1292,9 +1314,12 @@ async def _addAllLayrRead(self):
12921314
async def initServiceRuntime(self):
12931315

12941316
# do any post-nexus initialization here...
1317+
await self._initJsonStor()
1318+
12951319
if self.isactive:
12961320
await self._checkNexsIndx()
12971321
await self._checkLayerModels()
1322+
12981323
await self._initCoreMods()
12991324
await self._initStormSvcs()
13001325

@@ -3009,14 +3034,14 @@ async def _initCoreHive(self):
30093034
await self.stormvars.set(s_stormlib_cell.runtime_fixes_key, s_stormlib_cell.getMaxHotFixes())
30103035
self.onfini(self.stormvars)
30113036

3012-
async def _initCoreJsonStor(self):
3037+
async def _initJsonStor(self):
30133038

30143039
self.jsonurl = self.conf.get('jsonstor')
30153040
if self.jsonurl is not None:
30163041
self.jsonstor = await s_telepath.Client.anit(self.jsonurl)
30173042
else:
30183043
path = os.path.join(self.dirn, 'jsonstor')
3019-
self.jsonstor = await s_jsonstor.JsonStorCell.anit(path)
3044+
self.jsonstor = await s_jsonstor.JsonStorCell.anit(path, parent=self)
30203045

30213046
self.onfini(self.jsonstor)
30223047

@@ -3044,42 +3069,49 @@ async def getJsonObjProp(self, path, prop):
30443069
async def delJsonObj(self, path):
30453070
if self.jsonurl is not None:
30463071
await self.jsonstor.waitready()
3047-
return await self.jsonstor.delPathObj(path)
3048-
return await self._delJsonObj(path)
3072+
return await self.jsonstor.delPathObj(path)
30493073

30503074
async def delJsonObjProp(self, path, prop):
30513075
if self.jsonurl is not None:
30523076
await self.jsonstor.waitready()
3053-
return await self.jsonstor.delPathObjProp(path, prop)
3054-
return await self._delJsonObjProp(path, prop)
3077+
return await self.jsonstor.delPathObjProp(path, prop)
30553078

30563079
async def setJsonObj(self, path, item):
30573080
if self.jsonurl is not None:
30583081
await self.jsonstor.waitready()
3059-
return await self.jsonstor.setPathObj(path, item)
3060-
return await self._setJsonObj(path, item)
3082+
return await self.jsonstor.setPathObj(path, item)
30613083

30623084
async def setJsonObjProp(self, path, prop, item):
30633085
if self.jsonurl is not None:
30643086
await self.jsonstor.waitready()
3065-
return await self.jsonstor.setPathObjProp(path, prop, item)
3066-
return await self._setJsonObjProp(path, prop, item)
3087+
return await self.jsonstor.setPathObjProp(path, prop, item)
30673088

3068-
@s_nexus.Pusher.onPushAuto('json:del')
3069-
async def _delJsonObj(self, path):
3070-
return await self.jsonstor.delPathObj(path)
3089+
async def getUserNotif(self, indx):
3090+
if self.jsonurl is not None:
3091+
await self.jsonstor.waitready()
3092+
return await self.jsonstor.getUserNotif(indx)
30713093

3072-
@s_nexus.Pusher.onPushAuto('json:set')
3073-
async def _setJsonObj(self, path, item):
3074-
return await self.jsonstor.setPathObj(path, item)
3094+
async def delUserNotif(self, indx):
3095+
if self.jsonurl is not None:
3096+
await self.jsonstor.waitready()
3097+
return await self.jsonstor.delUserNotif(indx)
30753098

3076-
@s_nexus.Pusher.onPushAuto('json:del:prop')
3077-
async def _delJsonObjProp(self, path, prop):
3078-
return await self.jsonstor.delPathObjProp(path, prop)
3099+
async def addUserNotif(self, useriden, mesgtype, mesgdata=None):
3100+
if self.jsonurl is not None:
3101+
await self.jsonstor.waitready()
3102+
return await self.jsonstor.addUserNotif(useriden, mesgtype, mesgdata=mesgdata)
30793103

3080-
@s_nexus.Pusher.onPushAuto('json:set:prop')
3081-
async def _setJsonObjProp(self, path, prop, item):
3082-
return await self.jsonstor.setPathObjProp(path, prop, item)
3104+
async def iterUserNotifs(self, useriden, size=None):
3105+
if self.jsonurl is not None:
3106+
await self.jsonstor.waitready()
3107+
async for item in self.jsonstor.iterUserNotifs(useriden, size=size):
3108+
yield item
3109+
3110+
async def watchAllUserNotifs(self, offs=None):
3111+
if self.jsonurl is not None:
3112+
await self.jsonstor.waitready()
3113+
async for item in self.jsonstor.watchAllUserNotifs(offs=offs):
3114+
yield item
30833115

30843116
async def _initCoreAxon(self):
30853117
turl = self.conf.get('axon')

synapse/lib/cell.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
824824
VERSION = s_version.version
825825
VERSTRING = s_version.verstring
826826

827-
async def __anit__(self, dirn, conf=None, readonly=False):
827+
async def __anit__(self, dirn, conf=None, readonly=False, parent=None):
828828

829829
# phase 1
830830
if conf is None:
@@ -836,6 +836,7 @@ async def __anit__(self, dirn, conf=None, readonly=False):
836836
self.dirn = s_common.gendir(dirn)
837837

838838
self.auth = None
839+
self.cellparent = parent
839840
self.sessions = {}
840841
self.isactive = False
841842
self.inaugural = False
@@ -1027,9 +1028,10 @@ async def initServiceStorage(self):
10271028
pass
10281029

10291030
async def initNexusSubsystem(self):
1030-
mirror = self.conf.get('mirror')
1031-
await self.nexsroot.startup(mirror, celliden=self.iden)
1032-
await self.setCellActive(mirror is None)
1031+
if self.cellparent is None:
1032+
mirror = self.conf.get('mirror')
1033+
await self.nexsroot.startup(mirror, celliden=self.iden)
1034+
await self.setCellActive(mirror is None)
10331035

10341036
async def initServiceNetwork(self):
10351037

@@ -1123,6 +1125,9 @@ async def _ctorNexsRoot(self):
11231125
'''
11241126
Initialize a NexsRoot to use for the cell.
11251127
'''
1128+
if self.cellparent:
1129+
return self.cellparent.nexsroot
1130+
11261131
map_async = self.conf.get('nexslog:async')
11271132
return await s_nexus.NexsRoot.anit(self.dirn, donexslog=self.donexslog, map_async=map_async)
11281133

@@ -2003,7 +2008,7 @@ async def _initCellHive(self):
20032008
isnew = not self.slab.dbexists('hive')
20042009

20052010
db = self.slab.initdb('hive')
2006-
hive = await s_hive.SlabHive.anit(self.slab, db=db, nexsroot=self.nexsroot)
2011+
hive = await s_hive.SlabHive.anit(self.slab, db=db, nexsroot=self.getCellNexsRoot())
20072012
self.onfini(hive)
20082013

20092014
if isnew:
@@ -2042,10 +2047,16 @@ async def _initCellAuth(self):
20422047

20432048
return await self._initCellHiveAuth()
20442049

2050+
def getCellNexsRoot(self):
2051+
# the "cell scope" nexusroot only exists if we are *not* embedded
2052+
# (aka we dont have a self.cellparent)
2053+
if self.cellparent is None:
2054+
return self.nexsroot
2055+
20452056
async def _initCellHiveAuth(self):
20462057

20472058
node = await self.hive.open(('auth',))
2048-
auth = await s_hiveauth.Auth.anit(node, nexsroot=self.nexsroot)
2059+
auth = await s_hiveauth.Auth.anit(node, nexsroot=self.getCellNexsRoot())
20492060

20502061
self.onfini(auth.fini)
20512062
return auth

synapse/lib/jsonstor.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os
22
import asyncio
3+
import logging
34

45
import synapse.exc as s_exc
56
import synapse.common as s_common
@@ -10,6 +11,7 @@
1011
import synapse.lib.msgpack as s_msgpack
1112
import synapse.lib.lmdbslab as s_lmdbslab
1213

14+
logger = logging.getLogger(__name__)
1315

1416
class JsonStor(s_base.Base):
1517
'''
@@ -391,6 +393,28 @@ async def getsQueue(self, name, offs, size=None, cull=True, wait=True):
391393
async for item in self.cell.getsQueue(name, offs, size=size, cull=cull, wait=wait):
392394
yield item
393395

396+
@s_cell.adminapi()
397+
async def addUserNotif(self, useriden, mesgtype, mesgdata=None):
398+
return await self.cell.addUserNotif(useriden, mesgtype, mesgdata=mesgdata)
399+
400+
@s_cell.adminapi()
401+
async def getUserNotif(self, indx):
402+
return await self.cell.getUserNotif(indx)
403+
404+
@s_cell.adminapi()
405+
async def delUserNotif(self, indx):
406+
return await self.cell.delUserNotif(indx)
407+
408+
@s_cell.adminapi()
409+
async def iterUserNotifs(self, useriden, size=None):
410+
async for item in self.cell.iterUserNotifs(useriden, size=size):
411+
yield item
412+
413+
@s_cell.adminapi()
414+
async def watchAllUserNotifs(self, offs=None):
415+
async for item in self.cell.watchAllUserNotifs(offs=offs):
416+
yield item
417+
394418
class JsonStorCell(s_cell.Cell):
395419

396420
cellapi = JsonStorApi
@@ -402,6 +426,13 @@ async def initServiceStorage(self):
402426
self.onfini(self.jsonstor.fini)
403427
self.onfini(self.multique.fini)
404428

429+
self.notif_abrv_user = self.slab.getNameAbrv('users')
430+
self.notif_abrv_type = self.slab.getNameAbrv('types')
431+
432+
self.notifseqn = self.slab.getSeqn('notifs')
433+
self.notif_indx_usertime = self.slab.initdb('indx:user:time', dupsort=True)
434+
self.notif_indx_usertype = self.slab.initdb('indx:user:type', dupsort=True)
435+
405436
async def getPathList(self, path):
406437
async for item in self.jsonstor.getPathList(path):
407438
yield item
@@ -483,3 +514,66 @@ async def getsQueue(self, name, offs, size=None, cull=True, wait=True):
483514
await self.cullQueue(name, offs - 1)
484515
async for item in self.multique.gets(name, offs, size=size, wait=wait):
485516
yield item
517+
518+
async def addUserNotif(self, useriden, mesgtype, mesgdata=None):
519+
mesg = (useriden, s_common.now(), mesgtype, mesgdata)
520+
return await self._push('notif:add', mesg)
521+
522+
async def getUserNotif(self, indx):
523+
return self.notifseqn.get(indx)
524+
525+
@s_nexus.Pusher.onPush('notif:add', passitem=True)
526+
async def _addUserNotif(self, mesg, nexsitem):
527+
528+
indx = self.notifseqn.add(mesg, indx=nexsitem[0])
529+
indxbyts = s_common.int64en(indx)
530+
531+
useriden, mesgtime, mesgtype, mesgdata = mesg
532+
533+
userbyts = s_common.uhex(useriden)
534+
timebyts = s_common.int64en(mesgtime)
535+
typeabrv = self.notif_abrv_type.setBytsToAbrv(mesgtype.encode())
536+
537+
self.slab.put(userbyts + timebyts, indxbyts, db=self.notif_indx_usertime, dupdata=True)
538+
self.slab.put(userbyts + typeabrv + timebyts, indxbyts, db=self.notif_indx_usertype, dupdata=True)
539+
540+
return indx
541+
542+
@s_nexus.Pusher.onPushAuto('notif:del')
543+
async def delUserNotif(self, indx):
544+
envl = self.notifseqn.pop(indx)
545+
if envl is None:
546+
return
547+
548+
mesg = envl[1]
549+
useriden, mesgtime, mesgtype, mesgdata = mesg
550+
551+
indxbyts = s_common.int64en(indx)
552+
userbyts = s_common.uhex(useriden)
553+
timebyts = s_common.int64en(mesgtime)
554+
typeabrv = self.notif_abrv_type.setBytsToAbrv(mesgtype.encode())
555+
556+
self.slab.delete(userbyts + timebyts, indxbyts, db=self.notif_indx_usertime)
557+
self.slab.delete(userbyts + typeabrv + timebyts, indxbyts, db=self.notif_indx_usertype)
558+
559+
async def iterUserNotifs(self, useriden, size=None):
560+
# iterate user notifications backward
561+
userbyts = s_common.uhex(useriden)
562+
count = 0
563+
for _, indxbyts in self.slab.scanByPrefBack(userbyts, db=self.notif_indx_usertype):
564+
indx = s_common.int64un(indxbyts)
565+
mesg = self.notifseqn.getraw(indxbyts)
566+
yield (indx, mesg)
567+
count += 1
568+
if size is not None and count >= size:
569+
break
570+
571+
async def watchAllUserNotifs(self, offs=None):
572+
# yield only new notifications as they arrive
573+
if offs is None:
574+
offs = self.notifseqn.index()
575+
async for item in self.notifseqn.aiter(offs=offs, wait=True, timeout=120):
576+
yield item
577+
578+
# async def iterUserNotifsByTime(self, useriden, ival):
579+
# async def iterUserNotifsByType(self, useriden, mesgtype, ival=None):

synapse/lib/slabseqn.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,11 @@ def get(self, offs):
295295
if valu is not None:
296296
return s_msgpack.un(valu)
297297

298+
def getraw(self, byts):
299+
valu = self.slab.get(byts, db=self.db)
300+
if valu is not None:
301+
return s_msgpack.un(valu)
302+
298303
def slice(self, offs, size):
299304

300305
imax = size - 1

0 commit comments

Comments
 (0)