499 lines
11 KiB
Markdown
499 lines
11 KiB
Markdown
|
|
# Per-Service Statistics and Tracking Guide
|
||
|
|
|
||
|
|
Brainy provides comprehensive per-service statistics tracking, allowing you to monitor and analyze data from different services sharing the same storage backend.
|
||
|
|
|
||
|
|
## Table of Contents
|
||
|
|
- [Overview](#overview)
|
||
|
|
- [Configuration](#configuration)
|
||
|
|
- [Service Tracking](#service-tracking)
|
||
|
|
- [Statistics Methods](#statistics-methods)
|
||
|
|
- [Service Filtering](#service-filtering)
|
||
|
|
- [Use Cases](#use-cases)
|
||
|
|
- [API Reference](#api-reference)
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
When multiple services (e.g., `bluesky-package`, `github-package`, `scout-search`) share the same Brainy storage, per-service statistics help you:
|
||
|
|
|
||
|
|
- **Track individual service performance** - Monitor how many nouns/verbs each service has stored
|
||
|
|
- **Debug service-specific issues** - Isolate problems to specific services
|
||
|
|
- **Monitor service health** - Track last activity timestamps and error counts
|
||
|
|
- **Audit data sources** - Know which service created which data
|
||
|
|
- **Analyze usage patterns** - Understand how different services use the database
|
||
|
|
|
||
|
|
## Configuration
|
||
|
|
|
||
|
|
### Setting a Default Service
|
||
|
|
|
||
|
|
When creating a Brainy instance, specify a default service name:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const brainy = new BrainyData({
|
||
|
|
defaultService: 'github-package',
|
||
|
|
storage: {
|
||
|
|
// Storage configuration
|
||
|
|
}
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### Specifying Service Per Operation
|
||
|
|
|
||
|
|
You can override the default service for individual operations:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Add data with explicit service
|
||
|
|
await brainy.add(
|
||
|
|
{ content: 'repository data' },
|
||
|
|
{ noun: 'Repository' },
|
||
|
|
{ service: 'github-service' }
|
||
|
|
)
|
||
|
|
|
||
|
|
// Create relationship with service tracking
|
||
|
|
await brainy.relate(
|
||
|
|
sourceId,
|
||
|
|
targetId,
|
||
|
|
{ verb: 'stars' },
|
||
|
|
{ service: 'github-service' }
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Service Tracking
|
||
|
|
|
||
|
|
### Automatic Tracking
|
||
|
|
|
||
|
|
Brainy automatically tracks for each service:
|
||
|
|
- Total nouns created
|
||
|
|
- Total verbs created
|
||
|
|
- Total metadata entries
|
||
|
|
- First activity timestamp
|
||
|
|
- Last activity timestamp
|
||
|
|
- Operation counts (adds, updates, deletes)
|
||
|
|
- Error counts
|
||
|
|
|
||
|
|
### Service Activity States
|
||
|
|
|
||
|
|
Services can have three states:
|
||
|
|
- **`active`** - Had activity within the last hour
|
||
|
|
- **`inactive`** - No recent activity
|
||
|
|
- **`read-only`** - Only performs read operations (no writes)
|
||
|
|
|
||
|
|
## Statistics Methods
|
||
|
|
|
||
|
|
### List All Services
|
||
|
|
|
||
|
|
Get a list of all services that have written data:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const services = await brainy.listServices()
|
||
|
|
|
||
|
|
// Returns:
|
||
|
|
[
|
||
|
|
{
|
||
|
|
name: 'bluesky-package',
|
||
|
|
totalNouns: 12450,
|
||
|
|
totalVerbs: 34820,
|
||
|
|
totalMetadata: 12450,
|
||
|
|
firstActivity: '2024-01-01T00:00:00Z',
|
||
|
|
lastActivity: '2024-01-06T16:30:00Z',
|
||
|
|
status: 'active',
|
||
|
|
operations: {
|
||
|
|
adds: 12450,
|
||
|
|
updates: 230,
|
||
|
|
deletes: 0
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: 'github-package',
|
||
|
|
totalNouns: 45230,
|
||
|
|
totalVerbs: 89420,
|
||
|
|
totalMetadata: 45230,
|
||
|
|
lastActivity: '2024-01-06T16:25:00Z',
|
||
|
|
status: 'active'
|
||
|
|
}
|
||
|
|
]
|
||
|
|
```
|
||
|
|
|
||
|
|
### Get Service-Specific Statistics
|
||
|
|
|
||
|
|
Get detailed statistics for a single service:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const stats = await brainy.getServiceStatistics('github-package')
|
||
|
|
|
||
|
|
// Returns:
|
||
|
|
{
|
||
|
|
name: 'github-package',
|
||
|
|
totalNouns: 45230,
|
||
|
|
totalVerbs: 89420,
|
||
|
|
totalMetadata: 45230,
|
||
|
|
firstActivity: '2024-01-01T00:00:00Z',
|
||
|
|
lastActivity: '2024-01-06T16:25:00Z',
|
||
|
|
status: 'active',
|
||
|
|
operations: {
|
||
|
|
adds: 45230,
|
||
|
|
updates: 1250,
|
||
|
|
deletes: 50
|
||
|
|
},
|
||
|
|
errorCount: 5
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Filter Global Statistics by Service
|
||
|
|
|
||
|
|
Filter the global statistics to specific services:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Single service
|
||
|
|
const stats = await brainy.getStatistics({
|
||
|
|
service: 'bluesky-package'
|
||
|
|
})
|
||
|
|
|
||
|
|
// Multiple services
|
||
|
|
const stats = await brainy.getStatistics({
|
||
|
|
service: ['bluesky-package', 'github-package']
|
||
|
|
})
|
||
|
|
|
||
|
|
// Returns statistics with serviceBreakdown:
|
||
|
|
{
|
||
|
|
nounCount: 57680,
|
||
|
|
verbCount: 124240,
|
||
|
|
metadataCount: 57680,
|
||
|
|
serviceBreakdown: {
|
||
|
|
'bluesky-package': {
|
||
|
|
nounCount: 12450,
|
||
|
|
verbCount: 34820,
|
||
|
|
metadataCount: 12450
|
||
|
|
},
|
||
|
|
'github-package': {
|
||
|
|
nounCount: 45230,
|
||
|
|
verbCount: 89420,
|
||
|
|
metadataCount: 45230
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Service Filtering
|
||
|
|
|
||
|
|
### Filter Search Results
|
||
|
|
|
||
|
|
Filter search results to only include data from specific services:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const results = await brainy.search('javascript', 10, {
|
||
|
|
service: 'github-package'
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### Filter Noun Queries
|
||
|
|
|
||
|
|
Get nouns only from specific services:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const nouns = await brainy.getNouns({
|
||
|
|
filter: {
|
||
|
|
service: 'bluesky-package',
|
||
|
|
nounType: 'Post'
|
||
|
|
}
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### Filter Verb Queries
|
||
|
|
|
||
|
|
Get verbs only from specific services:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const verbs = await brainy.getVerbs({
|
||
|
|
filter: {
|
||
|
|
service: 'social-service',
|
||
|
|
verbType: 'follows'
|
||
|
|
}
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
## Use Cases
|
||
|
|
|
||
|
|
### Multi-Tenant Applications
|
||
|
|
|
||
|
|
When multiple applications share the same Brainy storage:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Application A
|
||
|
|
const brainyA = new BrainyData({
|
||
|
|
defaultService: 'app-a',
|
||
|
|
storage: sharedStorage
|
||
|
|
})
|
||
|
|
|
||
|
|
// Application B
|
||
|
|
const brainyB = new BrainyData({
|
||
|
|
defaultService: 'app-b',
|
||
|
|
storage: sharedStorage
|
||
|
|
})
|
||
|
|
|
||
|
|
// Monitor usage per application
|
||
|
|
const services = await brainyA.listServices()
|
||
|
|
for (const service of services) {
|
||
|
|
console.log(`${service.name}: ${service.totalNouns} nouns`)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Service Health Monitoring
|
||
|
|
|
||
|
|
Monitor the health and activity of different services:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async function monitorServiceHealth() {
|
||
|
|
const services = await brainy.listServices()
|
||
|
|
|
||
|
|
for (const service of services) {
|
||
|
|
if (service.status === 'inactive') {
|
||
|
|
console.warn(`Service ${service.name} is inactive`)
|
||
|
|
}
|
||
|
|
|
||
|
|
if (service.errorCount && service.errorCount > 100) {
|
||
|
|
console.error(`Service ${service.name} has high error count: ${service.errorCount}`)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check for stale services
|
||
|
|
if (service.lastActivity) {
|
||
|
|
const lastActivity = new Date(service.lastActivity)
|
||
|
|
const daysSinceActivity = (Date.now() - lastActivity.getTime()) / (1000 * 60 * 60 * 24)
|
||
|
|
|
||
|
|
if (daysSinceActivity > 7) {
|
||
|
|
console.warn(`Service ${service.name} hasn't been active for ${daysSinceActivity} days`)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Data Source Auditing
|
||
|
|
|
||
|
|
Track which service created specific data:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Add data with service tracking
|
||
|
|
const docId = await brainy.add(
|
||
|
|
{
|
||
|
|
content: 'Important document',
|
||
|
|
timestamp: new Date().toISOString()
|
||
|
|
},
|
||
|
|
{
|
||
|
|
noun: 'Document',
|
||
|
|
createdBy: { augmentation: 'audit-service' }
|
||
|
|
},
|
||
|
|
{ service: 'audit-service' }
|
||
|
|
)
|
||
|
|
|
||
|
|
// Later, verify the source
|
||
|
|
const stats = await brainy.getServiceStatistics('audit-service')
|
||
|
|
console.log(`Audit service created ${stats.totalNouns} documents`)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Performance Analysis
|
||
|
|
|
||
|
|
Analyze ingestion rates and patterns per service:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
async function analyzePerformance() {
|
||
|
|
const services = await brainy.listServices()
|
||
|
|
|
||
|
|
for (const service of services) {
|
||
|
|
if (!service.firstActivity || !service.lastActivity) continue
|
||
|
|
|
||
|
|
const start = new Date(service.firstActivity)
|
||
|
|
const end = new Date(service.lastActivity)
|
||
|
|
const durationHours = (end.getTime() - start.getTime()) / (1000 * 60 * 60)
|
||
|
|
|
||
|
|
const nounsPerHour = service.totalNouns / durationHours
|
||
|
|
const verbsPerHour = service.totalVerbs / durationHours
|
||
|
|
|
||
|
|
console.log(`${service.name} performance:`)
|
||
|
|
console.log(` Nouns/hour: ${nounsPerHour.toFixed(2)}`)
|
||
|
|
console.log(` Verbs/hour: ${verbsPerHour.toFixed(2)}`)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## API Reference
|
||
|
|
|
||
|
|
### Configuration Options
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface BrainyDataConfig {
|
||
|
|
/**
|
||
|
|
* Default service name for all operations
|
||
|
|
*/
|
||
|
|
defaultService?: string
|
||
|
|
|
||
|
|
// Other configuration options...
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Operation Options
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface OperationOptions {
|
||
|
|
/**
|
||
|
|
* Override the default service for this operation
|
||
|
|
*/
|
||
|
|
service?: string
|
||
|
|
|
||
|
|
// Other operation options...
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Service Statistics Type
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface ServiceStatistics {
|
||
|
|
/**
|
||
|
|
* Service name
|
||
|
|
*/
|
||
|
|
name: string
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Total number of nouns created by this service
|
||
|
|
*/
|
||
|
|
totalNouns: number
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Total number of verbs created by this service
|
||
|
|
*/
|
||
|
|
totalVerbs: number
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Total number of metadata entries
|
||
|
|
*/
|
||
|
|
totalMetadata: number
|
||
|
|
|
||
|
|
/**
|
||
|
|
* First activity timestamp
|
||
|
|
*/
|
||
|
|
firstActivity?: string
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Last activity timestamp
|
||
|
|
*/
|
||
|
|
lastActivity?: string
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Error count for this service
|
||
|
|
*/
|
||
|
|
errorCount?: number
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Operation breakdown
|
||
|
|
*/
|
||
|
|
operations?: {
|
||
|
|
adds: number
|
||
|
|
updates: number
|
||
|
|
deletes: number
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Service status
|
||
|
|
*/
|
||
|
|
status?: 'active' | 'inactive' | 'read-only'
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Best Practices
|
||
|
|
|
||
|
|
1. **Always specify a defaultService** when creating Brainy instances to ensure proper tracking
|
||
|
|
2. **Use consistent service names** across your application
|
||
|
|
3. **Monitor service health regularly** using `listServices()`
|
||
|
|
4. **Use service filtering** when debugging service-specific issues
|
||
|
|
5. **Track error counts** to identify problematic services
|
||
|
|
6. **Archive inactive services** periodically to maintain performance
|
||
|
|
|
||
|
|
## Migration Guide
|
||
|
|
|
||
|
|
If you have existing data without service tracking:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Set a default service for untracked data
|
||
|
|
const stats = await brainy.getStatistics()
|
||
|
|
|
||
|
|
// All existing data will be under the 'default' service
|
||
|
|
console.log(stats.serviceBreakdown['default'])
|
||
|
|
|
||
|
|
// Going forward, specify explicit services
|
||
|
|
const brainy = new BrainyData({
|
||
|
|
defaultService: 'my-app-v2',
|
||
|
|
// ...
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
## Limitations
|
||
|
|
|
||
|
|
- Service tracking is done at the statistics level, not embedded in each noun/verb
|
||
|
|
- Service filtering in search operations depends on metadata structure
|
||
|
|
- Historical service activity may not be available for data created before enabling tracking
|
||
|
|
- Service names should be kept reasonably short for performance
|
||
|
|
|
||
|
|
## Examples
|
||
|
|
|
||
|
|
### Complete Example: Multi-Service Dashboard
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { BrainyData } from '@soulcraft/brainy'
|
||
|
|
|
||
|
|
async function createServiceDashboard() {
|
||
|
|
const brainy = new BrainyData({
|
||
|
|
defaultService: 'dashboard',
|
||
|
|
storage: {
|
||
|
|
// Your storage configuration
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
await brainy.init()
|
||
|
|
|
||
|
|
// Get all services
|
||
|
|
const services = await brainy.listServices()
|
||
|
|
|
||
|
|
// Sort by most active
|
||
|
|
services.sort((a, b) => {
|
||
|
|
const aTime = a.lastActivity ? new Date(a.lastActivity).getTime() : 0
|
||
|
|
const bTime = b.lastActivity ? new Date(b.lastActivity).getTime() : 0
|
||
|
|
return bTime - aTime
|
||
|
|
})
|
||
|
|
|
||
|
|
// Display dashboard
|
||
|
|
console.log('=== Service Dashboard ===')
|
||
|
|
console.log(`Total Services: ${services.length}`)
|
||
|
|
console.log('')
|
||
|
|
|
||
|
|
for (const service of services) {
|
||
|
|
console.log(`Service: ${service.name}`)
|
||
|
|
console.log(` Status: ${service.status}`)
|
||
|
|
console.log(` Nouns: ${service.totalNouns}`)
|
||
|
|
console.log(` Verbs: ${service.totalVerbs}`)
|
||
|
|
|
||
|
|
if (service.lastActivity) {
|
||
|
|
const lastActivity = new Date(service.lastActivity)
|
||
|
|
console.log(` Last Active: ${lastActivity.toLocaleString()}`)
|
||
|
|
}
|
||
|
|
|
||
|
|
if (service.operations) {
|
||
|
|
console.log(` Operations:`)
|
||
|
|
console.log(` Adds: ${service.operations.adds}`)
|
||
|
|
console.log(` Updates: ${service.operations.updates}`)
|
||
|
|
console.log(` Deletes: ${service.operations.deletes}`)
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log('')
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get aggregate statistics
|
||
|
|
const totalStats = await brainy.getStatistics()
|
||
|
|
console.log('=== Total Statistics ===')
|
||
|
|
console.log(`Total Nouns: ${totalStats.nounCount}`)
|
||
|
|
console.log(`Total Verbs: ${totalStats.verbCount}`)
|
||
|
|
console.log(`Total Metadata: ${totalStats.metadataCount}`)
|
||
|
|
}
|
||
|
|
|
||
|
|
createServiceDashboard().catch(console.error)
|
||
|
|
```
|
||
|
|
|
||
|
|
This comprehensive per-service statistics feature enables better observability, debugging, and management of multi-service Brainy deployments.
|