koi-net 1.2.0b1__py3-none-any.whl → 1.2.0b2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of koi-net might be problematic. Click here for more details.
- koi_net/__init__.py +2 -1
- koi_net/assembler.py +82 -0
- koi_net/context.py +5 -13
- koi_net/core.py +51 -206
- koi_net/effector.py +26 -14
- koi_net/handshaker.py +3 -3
- koi_net/identity.py +2 -3
- koi_net/interfaces/entrypoint.py +5 -0
- koi_net/{worker.py → interfaces/worker.py} +7 -0
- koi_net/lifecycle.py +40 -32
- koi_net/logger.py +176 -0
- koi_net/network/error_handler.py +6 -6
- koi_net/network/event_queue.py +8 -6
- koi_net/network/graph.py +8 -8
- koi_net/network/poll_event_buffer.py +26 -0
- koi_net/network/request_handler.py +23 -28
- koi_net/network/resolver.py +13 -13
- koi_net/network/response_handler.py +74 -9
- koi_net/poller.py +7 -5
- koi_net/processor/event_worker.py +14 -18
- koi_net/processor/{default_handlers.py → handlers.py} +26 -25
- koi_net/processor/kobj_queue.py +3 -3
- koi_net/{kobj_worker.py → processor/kobj_worker.py} +12 -13
- koi_net/processor/{knowledge_pipeline.py → pipeline.py} +24 -27
- koi_net/protocol/api_models.py +5 -2
- koi_net/protocol/envelope.py +5 -6
- koi_net/protocol/model_map.py +61 -0
- koi_net/protocol/secure.py +8 -8
- koi_net/secure.py +5 -5
- koi_net/sentry.py +13 -0
- koi_net/server.py +36 -86
- {koi_net-1.2.0b1.dist-info → koi_net-1.2.0b2.dist-info}/METADATA +2 -1
- koi_net-1.2.0b2.dist-info/RECORD +52 -0
- koi_net/behaviors.py +0 -51
- koi_net/models.py +0 -14
- koi_net/poll_event_buffer.py +0 -17
- koi_net-1.2.0b1.dist-info/RECORD +0 -49
- {koi_net-1.2.0b1.dist-info → koi_net-1.2.0b2.dist-info}/WHEEL +0 -0
- {koi_net-1.2.0b1.dist-info → koi_net-1.2.0b2.dist-info}/entry_points.txt +0 -0
- {koi_net-1.2.0b1.dist-info → koi_net-1.2.0b2.dist-info}/licenses/LICENSE +0 -0
koi_net/network/resolver.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import structlog
|
|
2
2
|
import httpx
|
|
3
3
|
from rid_lib import RID
|
|
4
4
|
from rid_lib.core import RIDType
|
|
@@ -13,7 +13,7 @@ from ..protocol.api_models import ErrorResponse
|
|
|
13
13
|
from ..identity import NodeIdentity
|
|
14
14
|
from ..config import NodeConfig
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
log = structlog.stdlib.get_logger()
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class NetworkResolver:
|
|
@@ -45,7 +45,7 @@ class NetworkResolver:
|
|
|
45
45
|
def get_state_providers(self, rid_type: RIDType) -> list[KoiNetNode]:
|
|
46
46
|
"""Returns list of node RIDs which provide state for specified RID type."""
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
log.debug(f"Looking for state providers of {rid_type}")
|
|
49
49
|
provider_nodes = []
|
|
50
50
|
for node_rid in self.cache.list_rids(rid_types=[KoiNetNode]):
|
|
51
51
|
if node_rid == self.identity.rid:
|
|
@@ -56,17 +56,17 @@ class NetworkResolver:
|
|
|
56
56
|
node_profile = node_bundle.validate_contents(NodeProfile)
|
|
57
57
|
|
|
58
58
|
if (node_profile.node_type == NodeType.FULL) and (rid_type in node_profile.provides.state):
|
|
59
|
-
|
|
59
|
+
log.debug(f"Found provider {node_rid!r}")
|
|
60
60
|
provider_nodes.append(node_rid)
|
|
61
61
|
|
|
62
62
|
if not provider_nodes:
|
|
63
|
-
|
|
63
|
+
log.debug("Failed to find providers")
|
|
64
64
|
return provider_nodes
|
|
65
65
|
|
|
66
66
|
def fetch_remote_bundle(self, rid: RID) -> tuple[Bundle | None, KoiNetNode | None]:
|
|
67
67
|
"""Attempts to fetch a bundle by RID from known peer nodes."""
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
log.debug(f"Fetching remote bundle {rid!r}")
|
|
70
70
|
remote_bundle, node_rid = None, None
|
|
71
71
|
for node_rid in self.get_state_providers(type(rid)):
|
|
72
72
|
payload = self.request_handler.fetch_bundles(
|
|
@@ -74,18 +74,18 @@ class NetworkResolver:
|
|
|
74
74
|
|
|
75
75
|
if payload.bundles:
|
|
76
76
|
remote_bundle = payload.bundles[0]
|
|
77
|
-
|
|
77
|
+
log.debug(f"Got bundle from {node_rid!r}")
|
|
78
78
|
break
|
|
79
79
|
|
|
80
80
|
if not remote_bundle:
|
|
81
|
-
|
|
81
|
+
log.warning("Failed to fetch remote bundle")
|
|
82
82
|
|
|
83
83
|
return remote_bundle, node_rid
|
|
84
84
|
|
|
85
85
|
def fetch_remote_manifest(self, rid: RID) -> tuple[Bundle | None, KoiNetNode | None]:
|
|
86
86
|
"""Attempts to fetch a manifest by RID from known peer nodes."""
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
log.debug(f"Fetching remote manifest {rid!r}")
|
|
89
89
|
remote_manifest, node_rid = None, None
|
|
90
90
|
for node_rid in self.get_state_providers(type(rid)):
|
|
91
91
|
payload = self.request_handler.fetch_manifests(
|
|
@@ -93,11 +93,11 @@ class NetworkResolver:
|
|
|
93
93
|
|
|
94
94
|
if payload.manifests:
|
|
95
95
|
remote_manifest = payload.manifests[0]
|
|
96
|
-
|
|
96
|
+
log.debug(f"Got bundle from {node_rid!r}")
|
|
97
97
|
break
|
|
98
98
|
|
|
99
99
|
if not remote_manifest:
|
|
100
|
-
|
|
100
|
+
log.warning("Failed to fetch remote bundle")
|
|
101
101
|
|
|
102
102
|
return remote_manifest, node_rid
|
|
103
103
|
|
|
@@ -136,12 +136,12 @@ class NetworkResolver:
|
|
|
136
136
|
continue
|
|
137
137
|
|
|
138
138
|
if payload.events:
|
|
139
|
-
|
|
139
|
+
log.debug(f"Received {len(payload.events)} events from {node_rid!r}")
|
|
140
140
|
|
|
141
141
|
event_dict[node_rid] = payload.events
|
|
142
142
|
|
|
143
143
|
except httpx.ConnectError:
|
|
144
|
-
|
|
144
|
+
log.debug(f"Failed to reach node {node_rid!r}")
|
|
145
145
|
continue
|
|
146
146
|
|
|
147
147
|
return event_dict
|
|
@@ -1,10 +1,20 @@
|
|
|
1
|
-
import
|
|
1
|
+
import structlog
|
|
2
2
|
from rid_lib import RID
|
|
3
3
|
from rid_lib.types import KoiNetNode
|
|
4
4
|
from rid_lib.ext import Manifest, Cache
|
|
5
5
|
from rid_lib.ext.bundle import Bundle
|
|
6
6
|
|
|
7
|
+
from koi_net.network.poll_event_buffer import PollEventBuffer
|
|
8
|
+
from koi_net.processor.kobj_queue import KobjQueue
|
|
9
|
+
from koi_net.protocol.consts import BROADCAST_EVENTS_PATH, FETCH_BUNDLES_PATH, FETCH_MANIFESTS_PATH, FETCH_RIDS_PATH, POLL_EVENTS_PATH
|
|
10
|
+
from koi_net.protocol.envelope import SignedEnvelope
|
|
11
|
+
from koi_net.protocol.model_map import API_MODEL_MAP
|
|
12
|
+
from koi_net.secure import Secure
|
|
13
|
+
|
|
7
14
|
from ..protocol.api_models import (
|
|
15
|
+
ApiModels,
|
|
16
|
+
EventsPayload,
|
|
17
|
+
PollEvents,
|
|
8
18
|
RidsPayload,
|
|
9
19
|
ManifestsPayload,
|
|
10
20
|
BundlesPayload,
|
|
@@ -13,30 +23,81 @@ from ..protocol.api_models import (
|
|
|
13
23
|
FetchBundles,
|
|
14
24
|
)
|
|
15
25
|
|
|
16
|
-
|
|
26
|
+
log = structlog.stdlib.get_logger()
|
|
17
27
|
|
|
18
28
|
|
|
19
29
|
class ResponseHandler:
|
|
20
30
|
"""Handles generating responses to requests from other KOI nodes."""
|
|
21
31
|
|
|
22
32
|
cache: Cache
|
|
33
|
+
kobj_queue: KobjQueue
|
|
34
|
+
poll_event_buf: PollEventBuffer
|
|
23
35
|
|
|
24
36
|
def __init__(
|
|
25
37
|
self,
|
|
26
|
-
cache: Cache,
|
|
38
|
+
cache: Cache,
|
|
39
|
+
kobj_queue: KobjQueue,
|
|
40
|
+
poll_event_buf: PollEventBuffer,
|
|
41
|
+
secure: Secure
|
|
27
42
|
):
|
|
28
43
|
self.cache = cache
|
|
44
|
+
self.kobj_queue = kobj_queue
|
|
45
|
+
self.poll_event_buf = poll_event_buf
|
|
46
|
+
self.secure = secure
|
|
47
|
+
|
|
48
|
+
def handle_response(self, path: str, req: SignedEnvelope):
|
|
49
|
+
self.secure.validate_envelope(req)
|
|
50
|
+
|
|
51
|
+
response_map = {
|
|
52
|
+
BROADCAST_EVENTS_PATH: self.broadcast_events_handler,
|
|
53
|
+
POLL_EVENTS_PATH: self.poll_events_handler,
|
|
54
|
+
FETCH_RIDS_PATH: self.fetch_rids_handler,
|
|
55
|
+
FETCH_MANIFESTS_PATH: self.fetch_manifests_handler,
|
|
56
|
+
FETCH_BUNDLES_PATH: self.fetch_bundles_handler
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
response = response_map[path](req.payload, req.source_node)
|
|
60
|
+
|
|
61
|
+
if response is None:
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
return self.secure.create_envelope(
|
|
65
|
+
payload=response,
|
|
66
|
+
target=req.source_node
|
|
67
|
+
)
|
|
29
68
|
|
|
30
|
-
def
|
|
69
|
+
def broadcast_events_handler(self, req: EventsPayload, source: KoiNetNode):
|
|
70
|
+
log.info(f"Request to broadcast events, received {len(req.events)} event(s)")
|
|
71
|
+
|
|
72
|
+
for event in req.events:
|
|
73
|
+
self.kobj_queue.put_kobj(event=event, source=source)
|
|
74
|
+
|
|
75
|
+
def poll_events_handler(
|
|
76
|
+
self,
|
|
77
|
+
req: PollEvents,
|
|
78
|
+
source: KoiNetNode
|
|
79
|
+
) -> EventsPayload:
|
|
80
|
+
log.info(f"Request to poll events")
|
|
81
|
+
events = self.poll_event_buf.flush(source, limit=req.limit)
|
|
82
|
+
return EventsPayload(events=events)
|
|
83
|
+
|
|
84
|
+
def fetch_rids_handler(
|
|
85
|
+
self,
|
|
86
|
+
req: FetchRids,
|
|
87
|
+
source: KoiNetNode
|
|
88
|
+
) -> RidsPayload:
|
|
31
89
|
"""Returns response to fetch RIDs request."""
|
|
32
|
-
|
|
90
|
+
log.info(f"Request to fetch rids, allowed types {req.rid_types}")
|
|
33
91
|
rids = self.cache.list_rids(req.rid_types)
|
|
34
92
|
|
|
35
93
|
return RidsPayload(rids=rids)
|
|
36
94
|
|
|
37
|
-
def
|
|
95
|
+
def fetch_manifests_handler(self,
|
|
96
|
+
req: FetchManifests,
|
|
97
|
+
source: KoiNetNode
|
|
98
|
+
) -> ManifestsPayload:
|
|
38
99
|
"""Returns response to fetch manifests request."""
|
|
39
|
-
|
|
100
|
+
log.info(f"Request to fetch manifests, allowed types {req.rid_types}, rids {req.rids}")
|
|
40
101
|
|
|
41
102
|
manifests: list[Manifest] = []
|
|
42
103
|
not_found: list[RID] = []
|
|
@@ -50,9 +111,13 @@ class ResponseHandler:
|
|
|
50
111
|
|
|
51
112
|
return ManifestsPayload(manifests=manifests, not_found=not_found)
|
|
52
113
|
|
|
53
|
-
def
|
|
114
|
+
def fetch_bundles_handler(
|
|
115
|
+
self,
|
|
116
|
+
req: FetchBundles,
|
|
117
|
+
source: KoiNetNode
|
|
118
|
+
) -> BundlesPayload:
|
|
54
119
|
"""Returns response to fetch bundles request."""
|
|
55
|
-
|
|
120
|
+
log.info(f"Request to fetch bundles, requested rids {req.rids}")
|
|
56
121
|
|
|
57
122
|
bundles: list[Bundle] = []
|
|
58
123
|
not_found: list[RID] = []
|
koi_net/poller.py
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
|
|
2
2
|
import time
|
|
3
|
-
import
|
|
3
|
+
import structlog
|
|
4
|
+
|
|
5
|
+
from koi_net.interfaces.entrypoint import EntryPoint
|
|
4
6
|
from .processor.kobj_queue import KobjQueue
|
|
5
7
|
from .lifecycle import NodeLifecycle
|
|
6
8
|
from .network.resolver import NetworkResolver
|
|
7
9
|
from .config import NodeConfig
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
log = structlog.stdlib.get_logger()
|
|
10
12
|
|
|
11
13
|
|
|
12
|
-
class NodePoller:
|
|
14
|
+
class NodePoller(EntryPoint):
|
|
13
15
|
"""Manages polling based event loop for partial nodes."""
|
|
14
16
|
kobj_queue: KobjQueue
|
|
15
17
|
lifecycle: NodeLifecycle
|
|
@@ -18,10 +20,10 @@ class NodePoller:
|
|
|
18
20
|
|
|
19
21
|
def __init__(
|
|
20
22
|
self,
|
|
21
|
-
|
|
23
|
+
config: NodeConfig,
|
|
22
24
|
lifecycle: NodeLifecycle,
|
|
25
|
+
kobj_queue: KobjQueue,
|
|
23
26
|
resolver: NetworkResolver,
|
|
24
|
-
config: NodeConfig
|
|
25
27
|
):
|
|
26
28
|
self.kobj_queue = kobj_queue
|
|
27
29
|
self.lifecycle = lifecycle
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
import queue
|
|
2
2
|
import traceback
|
|
3
3
|
import time
|
|
4
|
-
import
|
|
4
|
+
import structlog
|
|
5
5
|
|
|
6
6
|
from rid_lib.ext import Cache
|
|
7
7
|
from rid_lib.types import KoiNetNode
|
|
8
8
|
|
|
9
9
|
from koi_net.config import NodeConfig
|
|
10
|
-
from koi_net.
|
|
11
|
-
from koi_net.network.event_queue import EventQueue
|
|
10
|
+
from koi_net.network.event_queue import EventQueue, QueuedEvent
|
|
12
11
|
from koi_net.network.request_handler import RequestHandler
|
|
13
|
-
from koi_net.poll_event_buffer import PollEventBuffer
|
|
12
|
+
from koi_net.network.poll_event_buffer import PollEventBuffer
|
|
14
13
|
from koi_net.protocol.event import Event
|
|
15
14
|
from koi_net.protocol.node import NodeProfile, NodeType
|
|
16
|
-
from koi_net.worker import ThreadWorker
|
|
15
|
+
from koi_net.interfaces.worker import ThreadWorker, STOP_WORKER
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
log = structlog.stdlib.get_logger()
|
|
19
18
|
|
|
20
19
|
|
|
21
20
|
class EventProcessingWorker(ThreadWorker):
|
|
@@ -28,10 +27,7 @@ class EventProcessingWorker(ThreadWorker):
|
|
|
28
27
|
request_handler: RequestHandler,
|
|
29
28
|
config: NodeConfig,
|
|
30
29
|
cache: Cache,
|
|
31
|
-
poll_event_buf: PollEventBuffer
|
|
32
|
-
queue_timeout: float = 0.1,
|
|
33
|
-
max_buf_len: int = 5,
|
|
34
|
-
max_wait_time: float = 1.0
|
|
30
|
+
poll_event_buf: PollEventBuffer
|
|
35
31
|
):
|
|
36
32
|
self.event_queue = event_queue
|
|
37
33
|
self.request_handler = request_handler
|
|
@@ -40,9 +36,9 @@ class EventProcessingWorker(ThreadWorker):
|
|
|
40
36
|
self.cache = cache
|
|
41
37
|
self.poll_event_buf = poll_event_buf
|
|
42
38
|
|
|
43
|
-
self.timeout =
|
|
44
|
-
self.max_buf_len =
|
|
45
|
-
self.max_wait_time =
|
|
39
|
+
self.timeout: float = 0.1
|
|
40
|
+
self.max_buf_len: int = 5
|
|
41
|
+
self.max_wait_time: float = 1.0
|
|
46
42
|
|
|
47
43
|
self.event_buffer = dict()
|
|
48
44
|
self.buffer_times = dict()
|
|
@@ -74,25 +70,25 @@ class EventProcessingWorker(ThreadWorker):
|
|
|
74
70
|
return True
|
|
75
71
|
|
|
76
72
|
else:
|
|
77
|
-
|
|
73
|
+
log.warning(f"Couldn't handle event {item.event!r} in queue, node {item.target!r} unknown to me")
|
|
78
74
|
return False
|
|
79
75
|
|
|
80
76
|
|
|
81
77
|
def run(self):
|
|
82
|
-
|
|
78
|
+
log.info("Started event worker")
|
|
83
79
|
while True:
|
|
84
80
|
now = time.time()
|
|
85
81
|
try:
|
|
86
82
|
item = self.event_queue.q.get(timeout=self.timeout)
|
|
87
83
|
|
|
88
84
|
try:
|
|
89
|
-
if item is
|
|
90
|
-
|
|
85
|
+
if item is STOP_WORKER:
|
|
86
|
+
log.info(f"Received 'STOP_WORKER' signal, flushing buffer...")
|
|
91
87
|
for target in self.event_buffer.keys():
|
|
92
88
|
self.flush_buffer(target, self.event_buffer[target])
|
|
93
89
|
return
|
|
94
90
|
|
|
95
|
-
|
|
91
|
+
log.info(f"Dequeued {item.event!r} -> {item.target!r}")
|
|
96
92
|
|
|
97
93
|
if not self.decide_event(item):
|
|
98
94
|
continue
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Implementation of default knowledge handlers."""
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import structlog
|
|
4
4
|
from rid_lib.ext import Bundle
|
|
5
5
|
from rid_lib.ext.utils import sha256_hash
|
|
6
6
|
from rid_lib.types import KoiNetNode, KoiNetEdge
|
|
@@ -9,10 +9,11 @@ from .handler import KnowledgeHandler, HandlerType, STOP_CHAIN
|
|
|
9
9
|
from .knowledge_object import KnowledgeObject
|
|
10
10
|
from ..context import HandlerContext
|
|
11
11
|
from ..protocol.event import Event, EventType
|
|
12
|
-
from ..protocol.edge import EdgeProfile, EdgeStatus, EdgeType
|
|
12
|
+
from ..protocol.edge import EdgeProfile, EdgeStatus, EdgeType
|
|
13
13
|
from ..protocol.node import NodeProfile
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
log = structlog.stdlib.get_logger()
|
|
16
|
+
|
|
16
17
|
|
|
17
18
|
# RID handlers
|
|
18
19
|
|
|
@@ -24,7 +25,7 @@ def basic_rid_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
|
24
25
|
RID is known to this node.
|
|
25
26
|
"""
|
|
26
27
|
if (kobj.rid == ctx.identity.rid and kobj.source):
|
|
27
|
-
|
|
28
|
+
log.debug("Don't let anyone else tell me who I am!")
|
|
28
29
|
return STOP_CHAIN
|
|
29
30
|
|
|
30
31
|
if kobj.event_type == EventType.FORGET:
|
|
@@ -45,17 +46,17 @@ def basic_manifest_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
|
45
46
|
|
|
46
47
|
if prev_bundle:
|
|
47
48
|
if kobj.manifest.sha256_hash == prev_bundle.manifest.sha256_hash:
|
|
48
|
-
|
|
49
|
+
log.debug("Hash of incoming manifest is same as existing knowledge, ignoring")
|
|
49
50
|
return STOP_CHAIN
|
|
50
51
|
if kobj.manifest.timestamp <= prev_bundle.manifest.timestamp:
|
|
51
|
-
|
|
52
|
+
log.debug("Timestamp of incoming manifest is the same or older than existing knowledge, ignoring")
|
|
52
53
|
return STOP_CHAIN
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
log.debug("RID previously known to me, labeling as 'UPDATE'")
|
|
55
56
|
kobj.normalized_event_type = EventType.UPDATE
|
|
56
57
|
|
|
57
58
|
else:
|
|
58
|
-
|
|
59
|
+
log.debug("RID previously unknown to me, labeling as 'NEW'")
|
|
59
60
|
kobj.normalized_event_type = EventType.NEW
|
|
60
61
|
|
|
61
62
|
return kobj
|
|
@@ -78,7 +79,7 @@ def secure_profile_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
|
78
79
|
node_rid: KoiNetNode = kobj.rid
|
|
79
80
|
|
|
80
81
|
if sha256_hash(node_profile.public_key) != node_rid.hash:
|
|
81
|
-
|
|
82
|
+
log.warning(f"Public key hash mismatch for {node_rid!r}!")
|
|
82
83
|
return STOP_CHAIN
|
|
83
84
|
|
|
84
85
|
@KnowledgeHandler.create(
|
|
@@ -104,13 +105,13 @@ def edge_negotiation_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
|
104
105
|
if edge_profile.status != EdgeStatus.PROPOSED:
|
|
105
106
|
return
|
|
106
107
|
|
|
107
|
-
|
|
108
|
+
log.debug("Handling edge negotiation")
|
|
108
109
|
|
|
109
110
|
peer_rid = edge_profile.target
|
|
110
111
|
peer_bundle = ctx.cache.read(peer_rid)
|
|
111
112
|
|
|
112
113
|
if not peer_bundle:
|
|
113
|
-
|
|
114
|
+
log.warning(f"Peer {peer_rid!r} unknown to me")
|
|
114
115
|
return STOP_CHAIN
|
|
115
116
|
|
|
116
117
|
peer_profile = peer_bundle.validate_contents(NodeProfile)
|
|
@@ -125,11 +126,11 @@ def edge_negotiation_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
|
125
126
|
abort = False
|
|
126
127
|
if (edge_profile.edge_type == EdgeType.WEBHOOK and
|
|
127
128
|
peer_profile.node_type == NodeType.PARTIAL):
|
|
128
|
-
|
|
129
|
+
log.debug("Partial nodes cannot use webhooks")
|
|
129
130
|
abort = True
|
|
130
131
|
|
|
131
132
|
if not set(edge_profile.rid_types).issubset(provided_events):
|
|
132
|
-
|
|
133
|
+
log.debug("Requested RID types not provided by this node")
|
|
133
134
|
abort = True
|
|
134
135
|
|
|
135
136
|
if abort:
|
|
@@ -139,7 +140,7 @@ def edge_negotiation_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
|
139
140
|
|
|
140
141
|
else:
|
|
141
142
|
# approve edge profile
|
|
142
|
-
|
|
143
|
+
log.debug("Approving proposed edge")
|
|
143
144
|
edge_profile.status = EdgeStatus.APPROVED
|
|
144
145
|
updated_bundle = Bundle.generate(kobj.rid, edge_profile.model_dump())
|
|
145
146
|
|
|
@@ -148,7 +149,7 @@ def edge_negotiation_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
|
148
149
|
|
|
149
150
|
elif edge_profile.target == ctx.identity.rid:
|
|
150
151
|
if edge_profile.status == EdgeStatus.APPROVED:
|
|
151
|
-
|
|
152
|
+
log.debug("Edge approved by other node!")
|
|
152
153
|
|
|
153
154
|
|
|
154
155
|
# Network handlers
|
|
@@ -176,8 +177,8 @@ def node_contact_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
|
176
177
|
if not available_rid_types:
|
|
177
178
|
return
|
|
178
179
|
|
|
179
|
-
|
|
180
|
-
|
|
180
|
+
log.info("Identified a coordinator!")
|
|
181
|
+
log.info("Proposing new edge")
|
|
181
182
|
|
|
182
183
|
# already have an edge established
|
|
183
184
|
edge_rid = ctx.graph.get_edge(
|
|
@@ -217,7 +218,7 @@ def node_contact_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
|
217
218
|
edge_bundle = Bundle.generate(edge_rid, edge_profile.model_dump())
|
|
218
219
|
ctx.kobj_queue.put_kobj(bundle=edge_bundle)
|
|
219
220
|
|
|
220
|
-
|
|
221
|
+
log.info("Catching up on network state")
|
|
221
222
|
|
|
222
223
|
payload = ctx.request_handler.fetch_rids(
|
|
223
224
|
node=kobj.rid,
|
|
@@ -225,16 +226,16 @@ def node_contact_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
|
225
226
|
)
|
|
226
227
|
for rid in payload.rids:
|
|
227
228
|
if rid == ctx.identity.rid:
|
|
228
|
-
|
|
229
|
+
log.info("Skipping myself")
|
|
229
230
|
continue
|
|
230
231
|
if ctx.cache.exists(rid):
|
|
231
|
-
|
|
232
|
+
log.info(f"Skipping known RID {rid!r}")
|
|
232
233
|
continue
|
|
233
234
|
|
|
234
235
|
# marked as external since we are handling RIDs from another node
|
|
235
236
|
# will fetch remotely instead of checking local cache
|
|
236
237
|
ctx.kobj_queue.put_kobj(rid=rid, source=kobj.rid)
|
|
237
|
-
|
|
238
|
+
log.info("Done")
|
|
238
239
|
|
|
239
240
|
|
|
240
241
|
@KnowledgeHandler.create(HandlerType.Network)
|
|
@@ -260,12 +261,12 @@ def basic_network_output_filter(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
|
260
261
|
edge_profile = kobj.bundle.validate_contents(EdgeProfile)
|
|
261
262
|
|
|
262
263
|
if edge_profile.source == ctx.identity.rid:
|
|
263
|
-
|
|
264
|
+
log.debug(f"Adding edge target '{edge_profile.target!r}' to network targets")
|
|
264
265
|
kobj.network_targets.update([edge_profile.target])
|
|
265
266
|
involves_me = True
|
|
266
267
|
|
|
267
268
|
elif edge_profile.target == ctx.identity.rid:
|
|
268
|
-
|
|
269
|
+
log.debug(f"Adding edge source '{edge_profile.source!r}' to network targets")
|
|
269
270
|
kobj.network_targets.update([edge_profile.source])
|
|
270
271
|
involves_me = True
|
|
271
272
|
|
|
@@ -276,7 +277,7 @@ def basic_network_output_filter(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
|
276
277
|
allowed_type=type(kobj.rid)
|
|
277
278
|
)
|
|
278
279
|
|
|
279
|
-
|
|
280
|
+
log.debug(f"Updating network targets with '{type(kobj.rid)}' subscribers: {subscribers}")
|
|
280
281
|
kobj.network_targets.update(subscribers)
|
|
281
282
|
|
|
282
283
|
return kobj
|
|
@@ -294,5 +295,5 @@ def forget_edge_on_node_deletion(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
|
294
295
|
edge_profile = edge_bundle.validate_contents(EdgeProfile)
|
|
295
296
|
|
|
296
297
|
if kobj.rid in (edge_profile.source, edge_profile.target):
|
|
297
|
-
|
|
298
|
+
log.debug("Identified edge with forgotten node")
|
|
298
299
|
ctx.kobj_queue.put_kobj(rid=edge_rid, event_type=EventType.FORGET)
|
koi_net/processor/kobj_queue.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import structlog
|
|
2
2
|
from queue import Queue
|
|
3
3
|
from rid_lib.core import RID
|
|
4
4
|
from rid_lib.ext import Bundle, Manifest
|
|
@@ -6,7 +6,7 @@ from rid_lib.types import KoiNetNode
|
|
|
6
6
|
from ..protocol.event import Event, EventType
|
|
7
7
|
from .knowledge_object import KnowledgeObject
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
log = structlog.stdlib.get_logger()
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class KobjQueue:
|
|
@@ -48,4 +48,4 @@ class KobjQueue:
|
|
|
48
48
|
raise ValueError("One of 'rid', 'manifest', 'bundle', 'event', or 'kobj' must be provided")
|
|
49
49
|
|
|
50
50
|
self.q.put(_kobj)
|
|
51
|
-
|
|
51
|
+
log.debug(f"Queued {_kobj!r}")
|
|
@@ -1,38 +1,37 @@
|
|
|
1
1
|
import queue
|
|
2
2
|
import traceback
|
|
3
|
-
import
|
|
3
|
+
import structlog
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
from
|
|
7
|
-
from koi_net.
|
|
8
|
-
from koi_net.worker import ThreadWorker
|
|
5
|
+
from .pipeline import KnowledgePipeline
|
|
6
|
+
from .kobj_queue import KobjQueue
|
|
7
|
+
from koi_net.interfaces.worker import ThreadWorker, STOP_WORKER
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
log = structlog.stdlib.get_logger()
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
class KnowledgeProcessingWorker(ThreadWorker):
|
|
14
13
|
def __init__(
|
|
15
14
|
self,
|
|
16
15
|
kobj_queue: KobjQueue,
|
|
17
|
-
pipeline: KnowledgePipeline
|
|
18
|
-
timeout: float = 0.1
|
|
16
|
+
pipeline: KnowledgePipeline
|
|
19
17
|
):
|
|
20
18
|
self.kobj_queue = kobj_queue
|
|
21
19
|
self.pipeline = pipeline
|
|
22
|
-
self.timeout =
|
|
20
|
+
self.timeout: float = 0.1
|
|
21
|
+
|
|
23
22
|
super().__init__()
|
|
24
23
|
|
|
25
24
|
def run(self):
|
|
26
|
-
|
|
25
|
+
log.info("Started kobj worker")
|
|
27
26
|
while True:
|
|
28
27
|
try:
|
|
29
28
|
item = self.kobj_queue.q.get(timeout=self.timeout)
|
|
30
29
|
try:
|
|
31
|
-
if item is
|
|
32
|
-
|
|
30
|
+
if item is STOP_WORKER:
|
|
31
|
+
log.info("Received 'STOP_WORKER' signal, shutting down...")
|
|
33
32
|
return
|
|
34
33
|
|
|
35
|
-
|
|
34
|
+
log.info(f"Dequeued {item!r}")
|
|
36
35
|
|
|
37
36
|
self.pipeline.process(item)
|
|
38
37
|
finally:
|