lablup / lablup/backend.ai

Implement Service Layer API for Notification Event Emission

Open
#6,438 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
670
Forks
183
Avg merge
17h 7m
Merged PRs (30d)
358

Description

## User Story

As a Backend.AI service developer, I want a clean API to emit notification events from service layer, so that I can easily integrate notifications into business logic without dealing with low-level details.

## Context

This story provides high-level service layer APIs for emitting notifications, making it easy for services to publish events to the Notification Center.

## Functional Requirements

### 1. Service Layer API

Provide simple, type-safe API for services:

```python
# src/ai/backend/manager/api/notification_service.py

class NotificationService:
def __init__(self, redis_client, db_session):
self.publisher = NotificationPublisher(redis_client)
self.db = db_session

async def emit(
self,
event_type: str,
subject: str,
context: dict[str, Any],
*,
body: str | None = None,
severity: Literal["info", "warning", "error", "critical"] = "info",
source: str | None = None,
metadata: dict[str, Any] | None = None,
action_url: str | None = None,
actions: list[dict] | None = None,
) -> str:
\"\"\"
Emit a notification event.

Returns:
event_id: Unique identifier for the emitted event
\"\"\"
event = NotificationEvent(
event_id=f"{event_type}-{uuid4()}",
event_type=event_type,
timestamp=datetime.now(timezone.utc),
source=source or "manager",
subject=subject,
body=body,
severity=severity,
context=context,
metadata=metadata or {},
action_url=action_url,
actions=actions,
)

await self.publisher.publish(event)
return event.event_id
```

### 2. Helper Functions for Common Patterns

Provide convenience methods for frequent use cases:

```python
class NotificationService:
# ... existing methods ...

async def emit_task_completed(
self,
task_id: str,
success_count: int,
failure_count: int,
duration: float,
**extra_context
):
\"\"\"Emit background task completion notification\"\"\"
return await self.emit(
event_type="bgtask.completed",
subject=f"Task {task_id} completed",
severity="info" if failure_count == 0 else "warning",
context={
"task_id": task_id,
"success_count": success_count,
"failure_count": failure_count,
"total_count": success_count + failure_count,
"success_rate": (success_count / (success_count + failure_count) * 100)
if (success_count + failure_count) > 0 else 0,
"duration": duration,
**extra_context
},
action_url=f"/tasks/{task_id}"
)

async def emit_quota_warning(
self,
domain: str,
resource_type: str,
current: float,
limit: float,
threshold_percent: float,
):
\"\"\"Emit resource quota warning\"\"\"
return await self.emit(
event_type="quota.warning",
subject=f"Resource quota warning: {resource_type}",
severity="warning",
context={
"domain": domain,
"resource_type": resource_type,
"current": current,
"limit": limit,
"usage_percent": (current / limit * 100) if limit > 0 else 0,
"threshold_percent": threshold_percent,
}
)

async def emit_system_error(
self,
error_type: str,
error_message: str,
stack_trace: str | None = None,
**extra_context
):
\"\"\"Emit system error notification\"\"\"
return await self.emit(
event_type="system.error",
subject=f"System error: {error_type}",
body=error_message,
severity="error",
context={
"error_type": error_type,
"error_message": error_message,
"stack_trace": stack_trace,
**extra_context
}
)
```

### 3. Context Manager for Batch Emission

Support efficient batch emission:

```python
class NotificationBatch:
def __init__(self, service: NotificationService):
self.service = service
self.events: list[NotificationEvent] = []

async def __aenter__(self):
return self

async def __aexit__(self, exc_type, exc_val, exc_tb):
if self.events:
await self.service.publisher.publish_batch(self.events)

def add(self, event: NotificationEvent):
self.events.append(event)

# Usage
async with NotificationBatch(notification_service) as batch:
for item in items:
event = NotificationEvent(...)
batch.add(event)
# All events sent on exit
```

### 4. Integration with Existing Services

Example integration points:

```python
# In bgtask reporter
class BackgroundTaskReporter:
def __init__(self, ..., notification_service: NotificationService):
self.notification_service = notification_service

async def report_completion(self, ...):
# Existing logic
await self._update_task_state(...)

# New: Emit notification
await self.notification_service.emit_task_completed(
task_id=task_id,
success_count=success_count,
failure_count=failure_count,
duration=duration,
)
```

## Acceptance Criteria

- [ ] NotificationService class with emit() method
- [ ] Helper methods for common event types
- [ ] Batch emission context manager
- [ ] Type hints and proper documentation
- [ ] Dependency injection via SharedConfig or Factory
- [ ] Unit tests with mock publisher
- [ ] Integration test with Redis
- [ ] Example usage in documentation

## Related Issues

Epic: BA-302
Depends on: BA-2861 (Core infrastructure)

JIRA Issue: BA-2863

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.