Skip to content

Commit 173fda2

Browse files
authored
Merge pull request #10 from starwit/feature/AB#1745-multiple-streams
Feature/ab#1745 support multiple streams
2 parents b385ae9 + 655291b commit 173fda2

11 files changed

Lines changed: 65 additions & 50 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SAE Redis Writer
22

3-
This component is part of the Starwit Awareness Engine (SAE). See umbrella repo here: https://github.com/starwit/vision-pipeline-k8s
3+
This component is part of the Starwit Awareness Engine (SAE). See umbrella repo here: https://github.com/starwit/starwit-awareness-engine
44

55
The intention of this stage is to write received messages to a different redis/[valkey](https://valkey.io/) instance. This means data created by SAE can be transfered to a backend.
66
It transparently forwards all messages it receives, regardless of their type, to the configured Redis instance.

deployment/valkey/valkey.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
protected-mode no

doc/DEV_README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Docker
44

5-
You can use [docker_build.sh](docker_build.sh) and [docker_publish.sh](docker_publish.sh) to build and publish Docker images.
5+
You can use [docker_build.sh](docker_build.sh) to build an image for local testing.
66

77
Once build you can run Docker image locally like so:
88
```bash

docker_build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
#!/bin/bash
22

3-
docker build -t starwitorg/sae-redis-writer:$(poetry version --short) .
3+
docker build -t starwitorg/sae-redis-writer:local .

docker_push.sh

Lines changed: 0 additions & 3 deletions
This file was deleted.

poetry.lock

Lines changed: 16 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ readme = "README.md"
1010
[tool.poetry.dependencies]
1111
python = "^3.10"
1212
pydantic = "^2.0.0"
13-
visionapi = { git = "https://github.com/starwit/vision-api.git", subdirectory = "python/visionapi", tag = "3.2.0" }
13+
visionapi = { git = "https://github.com/starwit/vision-api.git", subdirectory = "python/visionapi", tag = "3.4.0" }
1414
visionlib = { git = "https://github.com/starwit/vision-lib.git", subdirectory = "python", tag = "0.12.0" }
1515
redis = "^5.0.0"
1616
pydantic-settings = "^2.0.3"

rediswriter/config.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,25 @@
99
class RedisConfig(BaseModel):
1010
host: str = 'localhost'
1111
port: Annotated[int, Field(ge=1, le=65536)] = 6379
12-
input_stream_prefix: str = 'objecttracker'
1312

1413
class TargetRedisConfig(BaseModel):
1514
host: str
1615
port: Annotated[int, Field(ge=1, le=65536)]
17-
output_stream_prefix: str = 'output'
1816
buffer_length: Annotated[int, Field(ge=1)] = 100
1917
target_stream_maxlen: Annotated[int, Field(ge=1)] = 100
2018
tls: bool = False
19+
20+
class MappingConfig(BaseModel):
21+
source: str = None
22+
target: str = None
2123

2224
class RedisWriterConfig(BaseSettings):
2325
log_level: LogLevel = LogLevel.WARNING
2426
redis: RedisConfig = RedisConfig()
2527
target_redis: TargetRedisConfig
26-
stream_ids: List[str] = ['stream1']
2728
remove_frame_data: bool = True
2829
prometheus_port: Annotated[int, Field(gt=1024, le=65536)] = 8000
30+
mapping_config: List[MappingConfig]
2931

3032
model_config = SettingsConfigDict(env_nested_delimiter='__')
3133

rediswriter/sender.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ class BufferEntry(NamedTuple):
3535
class Sender:
3636
def __init__(self, config: RedisWriterConfig) -> None:
3737
self._config = config.target_redis
38-
self._stream_ids = config.stream_ids
3938
logger.setLevel(config.log_level.value)
4039

4140
self._buffer: Deque[BufferEntry] = deque(maxlen=self._config.buffer_length)

rediswriter/stage.py

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import Dict
66

77
from prometheus_client import Counter, start_http_server
8+
from visionapi.common_pb2 import TypeMessage, MessageType
89
from visionapi.sae_pb2 import SaeMessage
910
from visionlib.pipeline.consumer import RedisConsumer
1011
from visionlib.pipeline.formats import is_sae_message
@@ -17,10 +18,6 @@
1718

1819
FRAME_COUNTER = Counter('redis_writer_frame_counter', 'How many frames have been consumed from the Redis input stream')
1920

20-
class MessageType(Enum):
21-
SAE = 1
22-
OTHER = 2
23-
2421
def run_stage():
2522

2623
stop_event = threading.Event()
@@ -46,9 +43,11 @@ def sig_handler(signum, _):
4643
logger.info(f'Starting redis writer stage. Config: {CONFIG.model_dump_json(indent=2)}')
4744

4845
redis_writer = RedisWriter(CONFIG)
49-
50-
consumer = RedisConsumer(CONFIG.redis.host, CONFIG.redis.port,
51-
stream_keys=[f'{CONFIG.redis.input_stream_prefix}:{id}' for id in CONFIG.stream_ids])
46+
47+
stream_mapping = map_config(CONFIG.mapping_config)
48+
49+
consumer = RedisConsumer(CONFIG.redis.host, CONFIG.redis.port, stream_mapping.keys())
50+
logger.debug(f"Listening to stream keys {stream_mapping.keys()}")
5251

5352
sender = Sender(CONFIG)
5453

@@ -62,20 +61,23 @@ def sig_handler(signum, _):
6261
if stream_key is None:
6362
continue
6463

65-
stream_id = stream_key.split(':')[1]
64+
stream_id = stream_key.split(':')[1]
6665

6766
FRAME_COUNTER.inc()
67+
logger.debug(f'Received message on stream {stream_id}')
6868

6969
# Detect stream type by analyzing first message
70-
if not stream_id in message_type_by_stream:
71-
msg = SaeMessage()
70+
if not stream_id in message_type_by_stream:
71+
msg = TypeMessage()
7272
msg.ParseFromString(proto_data)
73-
if is_sae_message(msg):
74-
msg_type = MessageType.SAE
73+
if msg.type != MessageType.UNSPECIFIED:
74+
message_type_by_stream[stream_id] = msg.type
7575
else:
76-
msg_type = MessageType.OTHER
77-
message_type_by_stream[stream_id] = msg_type
78-
logger.info(f'Detected message type {msg_type.name} on stream {stream_id}')
76+
# if message type can't be determined, messages are NOT forwarded
77+
continue
78+
79+
type = MessageType.Name(msg.type)
80+
logger.info(f'Detected message type {type} on stream {stream_id}')
7981

8082
# Only process SaeMessage messages, otherwise pass verbatim
8183
if message_type_by_stream[stream_id] == MessageType.SAE:
@@ -86,4 +88,17 @@ def sig_handler(signum, _):
8688
if output_proto_data is None:
8789
continue
8890

89-
send(f'{CONFIG.target_redis.output_stream_prefix}:{stream_id}', output_proto_data)
91+
target_stream = stream_mapping.get(stream_key)
92+
send(target_stream, output_proto_data)
93+
94+
95+
def map_config(mapping_config):
96+
stream_mapping = {}
97+
for mapping in mapping_config:
98+
if mapping.source == None:
99+
continue
100+
if mapping.target == None:
101+
stream_mapping[mapping.source] = mapping.source
102+
else:
103+
stream_mapping[mapping.source] = mapping.target
104+
return stream_mapping

0 commit comments

Comments
 (0)