temporal

package
v1.1.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 25, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package temporal - Decay integration for adaptive memory retention.

DecayIntegration modifies NornicDB's decay system based on temporal patterns:

  • Frequently accessed nodes decay SLOWER (important memories persist)
  • Rarely accessed nodes decay FASTER (forgotten memories fade)
  • Nodes with daily patterns maintain longer (routine knowledge)
  • Burst-accessed nodes get temporary boost (current focus)

This creates a more human-like memory system where:

  • Things you use often stay fresh
  • Things you forget naturally fade
  • Context matters (current session nodes are prioritized)

Integration points:

  • DecayManager: Call GetDecayModifier() to adjust decay rate
  • ArchiveManager: Call ShouldArchive() to identify cold nodes
  • SearchRanker: Call GetRelevanceBoost() to rank results

ELI12 (Explain Like I'm 12)

Your brain forgets things! But it's SMART about what it forgets:

🧠 Your best friend's name?     → NEVER forget (use it daily!)
🧠 What you had for lunch today → Remember for a bit, then forget
🧠 Random fact from 5 years ago → Probably already forgot it

DecayIntegration makes the database work like your brain:

The "decay" is like forgetting. Every memory slowly fades over time. But the Kalman filter velocity tells us HOW to adjust the forgetting speed:

📈 Velocity positive (accessing MORE often):
   "Hey, you're using this a lot lately - slow down the forgetting!"
   Decay multiplier: 0.1 (10x slower decay)

📉 Velocity negative (accessing LESS often):
   "You used to look at this every day, now it's been weeks..."
   Decay multiplier: 2.0 (2x faster decay)

📊 Velocity stable:
   "Normal usage pattern, normal forgetting speed"
   Decay multiplier: 1.0 (normal decay)

Special cases:

🔥 BURST: Looking at something 10 times RIGHT NOW?
   → "Super important right now!" → Nearly zero decay

📅 DAILY PATTERN: Access every morning at 9am?
   → "Part of your routine!" → Slower decay

❄️ COLD: Haven't touched in 2 weeks?
   → "Probably not important anymore" → Faster decay, maybe archive

The Kalman filter smooths out noise. If you access something once by accident, it doesn't suddenly become "important". It waits to see a TREND.

Package temporal - Pattern detection for cyclic access patterns.

PatternDetector identifies recurring access patterns such as:

  • Daily patterns (e.g., accessed every morning at 9am)
  • Weekly patterns (e.g., accessed on Mondays)
  • Burst patterns (clusters of rapid accesses)
  • Decay patterns (gradually decreasing access)

ELI12 (Explain Like I'm 12)

Imagine you have a favorite YouTube video. The PatternDetector notices:

📊 Daily Pattern: "You watch this video every day at 7pm after dinner"
📊 Weekly Pattern: "You binge-watch on Saturdays"
📊 Burst Pattern: "You're watching 10 videos right NOW - probably bored!"
📊 Growing: "You're watching more and more cat videos each week"
📊 Decaying: "You used to watch daily, now it's been 2 weeks..."

The detector counts how often you do things at each hour (0-23) and each day (Sunday-Saturday). If one hour has WAY more than others, that's a pattern!

Example:

Hour  0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 20 21 22 23
Count 0  0  0  0  0  0  0  0  2  45  3  2  1  0  1  2  1  0  0  0  1  0  0  0
                                 ^^
                           "9am is clearly your jam!"

The Kalman filter velocity tells us if you're watching MORE or LESS over time. Positive velocity = growing interest, Negative velocity = losing interest.

Package temporal - Query load prediction for resource scaling.

QueryLoadPredictor tracks query volume trends and predicts future load using KalmanVelocity filters. This enables:

  • Predicting upcoming query spikes
  • Detecting load trends (increasing/decreasing)
  • Triggering pre-emptive resource scaling
  • Identifying query patterns (peak hours, burst events)

Use cases:

  • Auto-scale database connections
  • Pre-warm caches before predicted spikes
  • Alert on unusual load patterns
  • Capacity planning

Example usage:

predictor := temporal.NewQueryLoadPredictor(temporal.DefaultLoadConfig())

// Record each query
predictor.RecordQuery()

// Get current load prediction
prediction := predictor.GetPrediction()
fmt.Printf("Current QPS: %.1f, Predicted (5min): %.1f\n",
    prediction.CurrentQPS, prediction.PredictedQPS)

// Check if scaling needed
if predictor.ShouldScaleUp(100) { // threshold QPS
    triggerScaleUp()
}

ELI12 (Explain Like I'm 12)

Imagine you're running a lemonade stand. You want to know:

🍋 "How many customers are coming right now?" (Current QPS)
🍋 "Will it get busier or slower?" (Trend)
🍋 "Should I make more lemonade NOW?" (Scale up prediction)

The QueryLoadPredictor counts how many "questions" (queries) the database gets every second. It's like counting customers:

Second 1: 10 queries
Second 2: 12 queries
Second 3: 15 queries
Second 4: 20 queries
→ "Whoa, we're getting BUSIER! Velocity is positive!" 📈

The Kalman filter smooths out the bumps:

Raw counts:   10, 12, 50, 11, 13  (that 50 was a weird spike!)
Filtered:     10, 11, 15, 13, 13  (smoothed - ignores the spike)

Why filter? Because ONE busy second doesn't mean you need to panic! The filter asks: "Is this a REAL trend or just random noise?"

Predictions:

Current: 50 QPS (queries per second)
Velocity: +5 QPS/second (getting busier)
Predicted in 5 min: 50 + (5 × 300) = 1550 QPS! 😱
→ "Better scale up NOW before we're overwhelmed!"

Anomaly detection:

Normal: 50 QPS
Suddenly: 500 QPS ← "SPIKE! Something's happening!"
Suddenly: 5 QPS ← "DROP! Did something break?"

Peak hour detection:

Hour    0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 20 21 22 23
Count   2  1  1  1  2  5  15 30 50 45 40 30 35 40 50 45 40 35 25 20 15 10 5  3
                              ^^^^^^^^^^^^
                    "Peak hours are 8-10am and 2-4pm"

This lets us PREPARE before the rush! Pre-warm caches at 7:30am! 🚀

Package temporal - Relationship evolution tracking for dynamic graphs.

RelationshipEvolution tracks how edge weights change over time using KalmanVelocity filters. This enables:

  • Detecting strengthening relationships (increasing co-access)
  • Detecting weakening relationships (decreasing relevance)
  • Predicting future relationship strength
  • Identifying emerging connections

Use cases:

  • Recommend strengthening edges for pre-fetching
  • Prune weakening edges to save memory
  • Detect new relationship patterns automatically
  • Power dynamic graph visualizations

Example usage:

re := temporal.NewRelationshipEvolution(temporal.DefaultRelationshipConfig())

// Record co-access (updates edge weight)
re.RecordCoAccess("node-1", "node-2", 1.0)

// Get relationship trend
trend := re.GetTrend("node-1", "node-2")
fmt.Printf("Relationship is %s (velocity: %.3f)\n", trend.Direction, trend.Velocity)

// Predict future strength
future := re.PredictStrength("node-1", "node-2", 10)
fmt.Printf("In 10 steps, strength will be: %.3f\n", future)

ELI12 (Explain Like I'm 12)

Think about your friendships. Some get STRONGER over time, some fade:

👫 Best friend in 1st grade → Moved away → Don't talk anymore (WEAKENING)
👫 New kid at school → Hang out more → Now best friends! (STRENGTHENING)
👫 Neighbor → See them same amount → Just neighbors (STABLE)

RelationshipEvolution tracks how "connected" two things are, and whether that connection is growing or shrinking.

In a database, "relationships" are like friendships between data:

📚 "JavaScript" ←→ "React" : Strong connection (always accessed together)
📚 "JavaScript" ←→ "Python": Weak connection (rarely together)

The Kalman filter tracks the TREND:

Week 1: JS+React accessed together 10 times
Week 2: JS+React accessed together 15 times
Week 3: JS+React accessed together 20 times
→ Velocity is POSITIVE! Relationship is STRENGTHENING! 📈

Week 1: JS+Python accessed together 5 times
Week 2: JS+Python accessed together 3 times
Week 3: JS+Python accessed together 1 time
→ Velocity is NEGATIVE! Relationship is WEAKENING! 📉

Why this matters:

✅ STRENGTHENING relationships: Pre-fetch! If you access JS, also load React
❌ WEAKENING relationships: Maybe delete the connection, save memory
🌱 EMERGING relationships: "Ooh, this is new and growing fast - watch it!"

The Kalman filter makes this smooth. One weird week doesn't change everything. It looks for REAL TRENDS, not random noise.

Package temporal - Session detection via velocity changes.

SessionDetector identifies user context switches by monitoring:

  • Time gaps between accesses
  • Sudden changes in access rate (velocity)
  • Pattern breaks (different time-of-day)

Sessions are important for:

  • Co-access inference (nodes accessed in same session are related)
  • Context-aware search (prioritize current session nodes)
  • Memory consolidation (sessions = semantic boundaries)

ELI12 (Explain Like I'm 12)

Think about how you use your phone. You might:

📱 Morning: Check weather → Check email → Check news (WORK SESSION)
☕ Coffee break
📱 Later: Instagram → TikTok → YouTube (FUN SESSION)

The SessionDetector notices when you switch between "modes":

How it detects session changes:

  1. TIME GAP: If you stop for 5+ minutes, that's probably a new session "You were looking at work stuff, went to lunch, now you're on games"

  2. VELOCITY CHANGE (the Kalman filter magic!): The filter tracks HOW FAST you're accessing things. If velocity suddenly changes, your "mode" changed!

    Before: accessing every 2 seconds (fast browsing) After: accessing every 30 seconds (reading something) → "Whoa, you slowed WAY down - new session!"

Why does this matter for memory?

Session 1: [Weather, Email, News] → These are probably related (work stuff)
Session 2: [Instagram, TikTok]    → These are probably related (fun stuff)

So if you search for "weather", we boost Email and News because they were accessed in the SAME SESSION. That's co-access inference!

Package temporal provides temporal pattern tracking and prediction for NornicDB.

This package tracks when nodes are accessed and uses Kalman filtering to:

  • Smooth noisy access patterns
  • Predict when nodes will be accessed next
  • Detect session boundaries (context switches)
  • Identify cyclic patterns (daily, weekly, etc.)

The temporal system integrates with NornicDB's decay system to:

  • Slow decay for frequently accessed nodes
  • Speed decay for abandoned nodes
  • Predict archival candidates

Example usage:

tracker := temporal.NewTracker(temporal.DefaultConfig())

// Record accesses
tracker.RecordAccess("node-123")
tracker.RecordAccess("node-456")

// Get predictions
prediction := tracker.PredictNextAccess("node-123")
fmt.Printf("Node likely accessed in %.1f seconds\n", prediction.SecondsUntil)

// Check for session change
if tracker.IsSessionBoundary("node-123") {
	fmt.Println("User context has changed!")
}

ELI12 (Explain Like I'm 12)

Imagine you're keeping a diary of when your friends visit:

📅 Monday:    Sarah came at 3pm
📅 Tuesday:   Sarah came at 3pm
📅 Wednesday: Sarah came at 3:15pm
📅 Thursday:  ???

The Tracker is like a smart diary that notices patterns:

🧠 "Sarah visits around 3pm every day"
🧠 "Mike comes on weekends"
🧠 "Random friend hasn't visited in 2 weeks - maybe forgot about us?"

The Kalman filter is the "smart" part. When Sarah came at 3:15pm on Wednesday, instead of panicking ("OMG she's 15 minutes late!"), it smoothly updates:

Before: "Sarah comes at 3:00pm"
After:  "Sarah comes around 3:03pm" (small adjustment)

It's like averaging, but SMARTER because it:

  • Trusts patterns more than single weird events
  • Notices if someone is visiting MORE or LESS often (velocity)
  • Can predict: "At this rate, Sarah will visit at 2:50pm next week"

Why Kalman instead of simple averaging?

  • Simple average: "3pm + 3pm + 3:15pm = 3:05pm average"
  • Kalman: "3pm, 3pm, then 3:15pm... she might be getting later, or maybe it was just traffic. I'll say 3:03pm and watch for more data."

The Kalman filter LEARNS the trend (velocity) and uses it to predict!

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// MaxTrackedNodes - maximum number of nodes to track (LRU eviction)
	MaxTrackedNodes int

	// MinAccessesForPrediction - minimum accesses before making predictions
	MinAccessesForPrediction int

	// SessionTimeoutSeconds - gap that indicates a new session
	SessionTimeoutSeconds float64

	// VelocityChangeThreshold - velocity change that triggers session boundary
	VelocityChangeThreshold float64

	// FilterConfig for the underlying Kalman velocity filter
	FilterConfig filter.VelocityConfig

	// EnableAdaptiveFilter - use adaptive filter that switches modes
	EnableAdaptiveFilter bool

	// CleanupInterval - how often to clean up stale entries
	CleanupInterval time.Duration

	// MaxHistoryPerNode - maximum access history entries per node
	MaxHistoryPerNode int
}

Config holds temporal tracker configuration.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns sensible defaults for temporal tracking.

func HighPrecisionConfig

func HighPrecisionConfig() Config

HighPrecisionConfig returns config for high-precision temporal tracking.

func LowMemoryConfig

func LowMemoryConfig() Config

LowMemoryConfig returns config optimized for memory efficiency.

type DecayComponent

type DecayComponent struct {
	Name       string
	Multiplier float64
	Weight     float64
}

DecayComponent represents a single factor affecting decay.

type DecayIntegration

type DecayIntegration struct {
	// contains filtered or unexported fields
}

DecayIntegration manages decay rate modifications based on temporal data.

func NewDecayIntegration

func NewDecayIntegration(cfg DecayIntegrationConfig) *DecayIntegration

NewDecayIntegration creates a new decay integration system.

func NewDecayIntegrationWithComponents

func NewDecayIntegrationWithComponents(
	cfg DecayIntegrationConfig,
	tracker *Tracker,
	pattern *PatternDetector,
	session *SessionDetector,
) *DecayIntegration

NewDecayIntegrationWithComponents creates decay integration with existing components.

func (*DecayIntegration) GetColdNodes

func (di *DecayIntegration) GetColdNodes(limit int) []string

GetColdNodes returns nodes that are candidates for archival.

func (*DecayIntegration) GetDecayModifier

func (di *DecayIntegration) GetDecayModifier(nodeID string) DecayModifier

GetDecayModifier returns the decay rate modifier for a node.

func (*DecayIntegration) GetEffectiveDecayRate

func (di *DecayIntegration) GetEffectiveDecayRate(nodeID string) float64

GetEffectiveDecayRate returns the actual decay rate for a node.

func (*DecayIntegration) GetHotNodes

func (di *DecayIntegration) GetHotNodes(limit int) []string

GetHotNodes returns nodes that should be prioritized (slow decay).

func (*DecayIntegration) GetRelevanceBoost

func (di *DecayIntegration) GetRelevanceBoost(nodeID string) float64

GetRelevanceBoost returns a relevance boost for search ranking.

func (*DecayIntegration) GetStats

func (di *DecayIntegration) GetStats() DecayIntegrationStats

GetStats returns current statistics.

func (*DecayIntegration) RecordAccess

func (di *DecayIntegration) RecordAccess(nodeID string)

RecordAccess records an access and updates all temporal components.

func (*DecayIntegration) RecordAccessAt

func (di *DecayIntegration) RecordAccessAt(nodeID string, timestamp time.Time)

RecordAccessAt records an access at a specific time.

func (*DecayIntegration) Reset

func (di *DecayIntegration) Reset()

Reset clears all temporal data.

func (*DecayIntegration) ShouldArchive

func (di *DecayIntegration) ShouldArchive(nodeID string, currentScore float64, archiveThreshold float64) bool

ShouldArchive checks if a node should be archived based on temporal data.

type DecayIntegrationConfig

type DecayIntegrationConfig struct {
	// BaseDecayRate - the default decay rate per hour (0-1)
	BaseDecayRate float64

	// FrequentAccessBoost - how much to slow decay for frequent access (0.1 = 10x slower)
	FrequentAccessBoost float64

	// RareAccessPenalty - how much to speed decay for rare access (2.0 = 2x faster)
	RareAccessPenalty float64

	// DailyPatternBoost - boost for nodes with daily patterns
	DailyPatternBoost float64

	// BurstBoostDuration - how long burst boost lasts (seconds)
	BurstBoostDuration float64

	// BurstBoostMultiplier - decay multiplier during burst
	BurstBoostMultiplier float64

	// SessionBoostMultiplier - decay multiplier for current session nodes
	SessionBoostMultiplier float64

	// MinDecayMultiplier - minimum decay multiplier (prevent immortal nodes)
	MinDecayMultiplier float64

	// MaxDecayMultiplier - maximum decay multiplier (prevent instant death)
	MaxDecayMultiplier float64

	// VelocityWeight - how much velocity affects decay
	VelocityWeight float64

	// PatternWeight - how much patterns affect decay
	PatternWeight float64

	// RecencyWeight - how much recency affects decay
	RecencyWeight float64
}

DecayIntegrationConfig holds configuration for decay integration.

func AggressiveDecayConfig

func AggressiveDecayConfig() DecayIntegrationConfig

AggressiveDecayConfig returns config that forgets faster.

func ConservativeDecayConfig

func ConservativeDecayConfig() DecayIntegrationConfig

ConservativeDecayConfig returns config that preserves more memories.

func DefaultDecayIntegrationConfig

func DefaultDecayIntegrationConfig() DecayIntegrationConfig

DefaultDecayIntegrationConfig returns sensible defaults.

type DecayIntegrationStats

type DecayIntegrationStats struct {
	TrackedNodes      int
	ActiveSessions    int
	TotalAccesses     int64
	AverageMultiplier float64
}

GetStats returns statistics for the decay integration.

type DecayModifier

type DecayModifier struct {
	// Multiplier for decay rate (0.5 = half decay speed, 2.0 = double decay speed)
	Multiplier float64

	// Reason for the modification
	Reason string

	// Confidence in this modification (0-1)
	Confidence float64

	// Components that contributed to this modifier
	Components []DecayComponent
}

DecayModifier represents how decay should be adjusted.

type DetectedPattern

type DetectedPattern struct {
	Type       PatternType
	Confidence float64
	PeakHour   int       // 0-23, for daily patterns
	PeakDay    int       // 0-6 (Sunday=0), for weekly patterns
	Period     float64   // Estimated period in seconds
	LastSeen   time.Time // When pattern was last observed
}

DetectedPattern holds information about a detected pattern.

type GlobalStats

type GlobalStats struct {
	TotalAccesses  int64
	TrackedNodes   int
	UptimeSeconds  float64
	AccessesPerSec float64
}

GlobalStats returns global tracking statistics.

type LoadConfig

type LoadConfig struct {
	// FilterConfig for the underlying Kalman velocity filter
	FilterConfig filter.VelocityConfig

	// BucketDurationSeconds - duration of each measurement bucket
	BucketDurationSeconds float64

	// SpikeThreshold - QPS velocity above which is a "spike"
	SpikeThreshold float64

	// DropThreshold - QPS velocity below which is a "drop"
	DropThreshold float64

	// AnomalyStdDevs - number of standard deviations for anomaly detection
	AnomalyStdDevs float64

	// ScaleUpThreshold - relative increase that suggests scaling up
	ScaleUpThreshold float64

	// ScaleDownThreshold - relative decrease that suggests scaling down
	ScaleDownThreshold float64

	// PeakDetectionWindow - hours to track for peak detection
	PeakDetectionWindow int
}

LoadConfig holds configuration for query load prediction.

func DefaultLoadConfig

func DefaultLoadConfig() LoadConfig

DefaultLoadConfig returns sensible defaults.

func HighSensitivityLoadConfig

func HighSensitivityLoadConfig() LoadConfig

HighSensitivityLoadConfig returns config for high-sensitivity detection.

type LoadPrediction

type LoadPrediction struct {
	// Current metrics
	CurrentQPS   float64 // Queries per second (smoothed)
	CurrentQPM   float64 // Queries per minute (smoothed)
	RawQPS       float64 // Unfiltered QPS
	TotalQueries int64

	// Trend
	Velocity float64 // Rate of change (positive = increasing load)
	Trend    string  // "increasing", "decreasing", "stable"

	// Predictions
	PredictedQPS5m  float64 // Predicted QPS in 5 minutes
	PredictedQPS15m float64 // Predicted QPS in 15 minutes
	PredictedQPS1h  float64 // Predicted QPS in 1 hour

	// Confidence
	Confidence float64

	// Time-of-day pattern
	PeakHour   int
	IsNearPeak bool

	// Anomaly detection
	IsAnomaly   bool
	AnomalyType string // "spike", "drop", "sustained_high", "sustained_low"

	// Timestamp
	Timestamp time.Time
}

LoadPrediction represents a query load prediction.

type LoadStats

type LoadStats struct {
	TotalQueries  int64
	UptimeSeconds float64
	AverageQPS    float64
	CurrentQPS    float64
	PeakQPS       float64
	PeakHour      int
}

LoadStats holds statistics about query load.

type NodeAccess

type NodeAccess struct {
	Timestamp time.Time
	SessionID string // Optional session identifier
}

NodeAccess represents a single access event.

type NodeStats

type NodeStats struct {
	NodeID string

	// Access counts
	TotalAccesses  int64
	AccessesInHour int
	AccessesInDay  int
	AccessesInWeek int

	// Timing
	FirstAccess     time.Time
	LastAccess      time.Time
	AverageInterval float64 // seconds between accesses

	// Predictions
	PredictedNextAccess  time.Time
	PredictionConfidence float64

	// Pattern detection
	HasDailyPattern  bool
	HasWeeklyPattern bool
	PeakHour         int // 0-23
	PeakDay          int // 0-6 (Sunday=0)

	// Session info
	CurrentSessionStart time.Time
	SessionCount        int

	// Filter state
	AccessRateVelocity float64 // rate of change of access frequency
}

NodeStats holds temporal statistics for a single node.

type PatternDetector

type PatternDetector struct {
	// contains filtered or unexported fields
}

PatternDetector detects access patterns for nodes.

func NewPatternDetector

func NewPatternDetector(cfg PatternDetectorConfig) *PatternDetector

NewPatternDetector creates a new pattern detector.

func (*PatternDetector) DetectPatterns

func (pd *PatternDetector) DetectPatterns(nodeID string, currentVelocity float64) []DetectedPattern

DetectPatterns analyzes access patterns for a node.

func (*PatternDetector) GetPeakAccessTime

func (pd *PatternDetector) GetPeakAccessTime(nodeID string) (hour int, day int, confidence float64)

GetPeakAccessTime returns the predicted best time to access a node.

func (*PatternDetector) HasPattern

func (pd *PatternDetector) HasPattern(nodeID string, patternType PatternType, velocity float64) bool

HasPattern checks if a node has a specific pattern type.

func (*PatternDetector) RecordAccess

func (pd *PatternDetector) RecordAccess(nodeID string, timestamp time.Time)

RecordAccess records an access for pattern analysis.

func (*PatternDetector) Reset

func (pd *PatternDetector) Reset()

Reset clears all pattern data.

func (*PatternDetector) ResetNode

func (pd *PatternDetector) ResetNode(nodeID string)

ResetNode clears pattern data for a specific node.

type PatternDetectorConfig

type PatternDetectorConfig struct {
	// MinSamplesForPattern - minimum accesses to detect patterns
	MinSamplesForPattern int

	// DailyConfidenceThreshold - min confidence to report daily pattern
	DailyConfidenceThreshold float64

	// WeeklyConfidenceThreshold - min confidence to report weekly pattern
	WeeklyConfidenceThreshold float64

	// BurstWindowSeconds - time window for burst detection
	BurstWindowSeconds float64

	// BurstMinAccesses - minimum accesses in window to be a burst
	BurstMinAccesses int

	// GrowthThreshold - velocity above which is "growing"
	GrowthThreshold float64

	// DecayThreshold - velocity below which is "decaying"
	DecayThreshold float64
}

PatternDetectorConfig holds configuration for pattern detection.

func DefaultPatternDetectorConfig

func DefaultPatternDetectorConfig() PatternDetectorConfig

DefaultPatternDetectorConfig returns sensible defaults.

type PatternType

type PatternType string

PatternType represents detected pattern types.

const (
	PatternNone     PatternType = "none"
	PatternDaily    PatternType = "daily"
	PatternWeekly   PatternType = "weekly"
	PatternBurst    PatternType = "burst"
	PatternDecaying PatternType = "decaying"
	PatternGrowing  PatternType = "growing"
)

type Prediction

type Prediction struct {
	NodeID          string
	PredictedTime   time.Time
	SecondsUntil    float64
	Confidence      float64
	BasedOnAccesses int
	AccessRateTrend string // "increasing", "stable", "decreasing"
}

Prediction represents a predicted future access.

type QueryLoadPredictor

type QueryLoadPredictor struct {
	// contains filtered or unexported fields
}

QueryLoadPredictor predicts query load using velocity tracking.

func NewQueryLoadPredictor

func NewQueryLoadPredictor(cfg LoadConfig) *QueryLoadPredictor

NewQueryLoadPredictor creates a new query load predictor.

func (*QueryLoadPredictor) GetLoadLevel

func (qlp *QueryLoadPredictor) GetLoadLevel(maxQPS float64) int

GetLoadLevel returns a simplified load level (0-5).

func (*QueryLoadPredictor) GetPrediction

func (qlp *QueryLoadPredictor) GetPrediction() LoadPrediction

GetPrediction returns the current load prediction.

func (*QueryLoadPredictor) GetStats

func (qlp *QueryLoadPredictor) GetStats() LoadStats

GetStats returns query load statistics.

func (*QueryLoadPredictor) PredictPeakTime

func (qlp *QueryLoadPredictor) PredictPeakTime() time.Time

PredictPeakTime predicts when the next peak will occur.

func (*QueryLoadPredictor) RecordQueries

func (qlp *QueryLoadPredictor) RecordQueries(count int)

RecordQueries records multiple query events (batch recording).

func (*QueryLoadPredictor) RecordQuery

func (qlp *QueryLoadPredictor) RecordQuery()

RecordQuery records a query event.

func (*QueryLoadPredictor) RecordQueryAt

func (qlp *QueryLoadPredictor) RecordQueryAt(timestamp time.Time)

RecordQueryAt records a query at a specific time.

func (*QueryLoadPredictor) Reset

func (qlp *QueryLoadPredictor) Reset()

Reset clears all load data.

func (*QueryLoadPredictor) ShouldScaleDown

func (qlp *QueryLoadPredictor) ShouldScaleDown(thresholdQPS float64, minQPS float64) bool

ShouldScaleDown checks if load is decreasing and below threshold.

func (*QueryLoadPredictor) ShouldScaleUp

func (qlp *QueryLoadPredictor) ShouldScaleUp(thresholdQPS float64) bool

ShouldScaleUp checks if load is increasing and above threshold.

type RelationshipConfig

type RelationshipConfig struct {
	// FilterConfig for the underlying Kalman velocity filter
	FilterConfig filter.VelocityConfig

	// MaxTrackedRelationships - maximum relationships to track (LRU eviction)
	MaxTrackedRelationships int

	// StrengthenThreshold - velocity above which is "strengthening"
	StrengthenThreshold float64

	// WeakenThreshold - velocity below which is "weakening"
	WeakenThreshold float64

	// MinObservationsForTrend - minimum observations before reporting trend
	MinObservationsForTrend int

	// DecayIdleRelationships - decay weight of idle relationships
	DecayIdleRelationships bool

	// IdleDecayRate - how much to decay per hour of inactivity
	IdleDecayRate float64
}

RelationshipConfig holds configuration for relationship evolution tracking.

func DefaultRelationshipConfig

func DefaultRelationshipConfig() RelationshipConfig

DefaultRelationshipConfig returns sensible defaults.

type RelationshipEvolution

type RelationshipEvolution struct {
	// contains filtered or unexported fields
}

RelationshipEvolution tracks edge weight changes over time.

func NewRelationshipEvolution

func NewRelationshipEvolution(cfg RelationshipConfig) *RelationshipEvolution

NewRelationshipEvolution creates a new relationship evolution tracker.

func (*RelationshipEvolution) DecayIdleRelationships

func (re *RelationshipEvolution) DecayIdleRelationships(maxIdleHours float64) int

DecayIdleRelationships applies decay to relationships not updated recently.

func (*RelationshipEvolution) GetEmergingRelationships

func (re *RelationshipEvolution) GetEmergingRelationships(limit int) []RelationshipTrend

GetEmergingRelationships returns new relationships with positive velocity.

func (*RelationshipEvolution) GetStats

GetStats returns statistics about relationship tracking.

func (*RelationshipEvolution) GetStrengtheningRelationships

func (re *RelationshipEvolution) GetStrengtheningRelationships(limit int) []RelationshipTrend

GetStrengtheningRelationships returns relationships that are getting stronger.

func (*RelationshipEvolution) GetTrend

func (re *RelationshipEvolution) GetTrend(sourceID, targetID string) *RelationshipTrend

GetTrend returns the evolution trend for a relationship.

func (*RelationshipEvolution) GetWeakeningRelationships

func (re *RelationshipEvolution) GetWeakeningRelationships(limit int) []RelationshipTrend

GetWeakeningRelationships returns relationships that are getting weaker.

func (*RelationshipEvolution) PredictStrength

func (re *RelationshipEvolution) PredictStrength(sourceID, targetID string, steps int) float64

PredictStrength predicts the relationship strength n steps ahead.

func (*RelationshipEvolution) RecordCoAccess

func (re *RelationshipEvolution) RecordCoAccess(sourceID, targetID string, weight float64)

RecordCoAccess records a co-access event between two nodes. weight should be 1.0 for simple co-access, or can be weighted.

func (*RelationshipEvolution) RecordCoAccessAt

func (re *RelationshipEvolution) RecordCoAccessAt(sourceID, targetID string, weight float64, timestamp time.Time)

RecordCoAccessAt records co-access at a specific time.

func (*RelationshipEvolution) Reset

func (re *RelationshipEvolution) Reset()

Reset clears all relationship data.

func (*RelationshipEvolution) ShouldPrune

func (re *RelationshipEvolution) ShouldPrune(sourceID, targetID string, threshold float64) bool

ShouldPrune checks if a relationship should be pruned (very weak and weakening).

func (*RelationshipEvolution) UpdateWeight

func (re *RelationshipEvolution) UpdateWeight(sourceID, targetID string, newWeight float64)

UpdateWeight updates the weight of an existing relationship. Use this for explicit weight updates (not just co-access).

type RelationshipStats

type RelationshipStats struct {
	TrackedRelationships int
	TotalUpdates         int64
	Strengthening        int
	Weakening            int
	Stable               int
	UptimeSeconds        float64
}

RelationshipStats holds statistics about relationship tracking.

type RelationshipTrend

type RelationshipTrend struct {
	// Direction: "strengthening", "weakening", "stable"
	Direction string

	// Velocity: rate of change (positive = strengthening)
	Velocity float64

	// CurrentStrength: current filtered weight
	CurrentStrength float64

	// PredictedStrength: predicted weight in 5 steps
	PredictedStrength float64

	// Confidence: confidence in the trend (0-1)
	Confidence float64

	// ObservationCount: number of weight updates
	ObservationCount int

	// LastUpdate: when the relationship was last updated
	LastUpdate time.Time
}

RelationshipTrend represents the evolution trend of a relationship.

type Session

type Session struct {
	ID        string
	StartTime time.Time
	EndTime   time.Time
	NodeIDs   []string // Nodes accessed in this session
	Duration  time.Duration
	IsCurrent bool
}

Session represents a detected user session.

type SessionDetector

type SessionDetector struct {
	// contains filtered or unexported fields
}

SessionDetector detects session boundaries from access patterns.

func NewSessionDetector

func NewSessionDetector(cfg SessionDetectorConfig) *SessionDetector

NewSessionDetector creates a new session detector.

func (*SessionDetector) AddListener

func (sd *SessionDetector) AddListener(listener func(SessionEvent))

AddListener adds a listener for session events.

func (*SessionDetector) GetActiveSessions

func (sd *SessionDetector) GetActiveSessions() []*Session

GetActiveSessions returns all currently active sessions.

func (*SessionDetector) GetCoAccessedNodes

func (sd *SessionDetector) GetCoAccessedNodes(nodeID string) []string

GetCoAccessedNodes returns nodes accessed in the same session.

func (*SessionDetector) GetCurrentSession

func (sd *SessionDetector) GetCurrentSession(nodeID string) *Session

GetCurrentSession returns the current session for a node.

func (*SessionDetector) GetSessionHistory

func (sd *SessionDetector) GetSessionHistory(nodeID string, limit int) []*Session

GetSessionHistory returns session history for a node.

func (*SessionDetector) GetVelocity

func (sd *SessionDetector) GetVelocity(nodeID string) float64

GetVelocity returns the current access rate velocity for a node.

func (*SessionDetector) IsSessionBoundary

func (sd *SessionDetector) IsSessionBoundary(nodeID string) bool

IsSessionBoundary checks if the last access was a session boundary.

func (*SessionDetector) RecordAccess

func (sd *SessionDetector) RecordAccess(nodeID string, timestamp time.Time) SessionEvent

RecordAccess records an access and checks for session boundaries.

func (*SessionDetector) Reset

func (sd *SessionDetector) Reset()

Reset clears all session data.

type SessionDetectorConfig

type SessionDetectorConfig struct {
	// TimeGapThresholdSeconds - gap that triggers new session
	TimeGapThresholdSeconds float64

	// VelocityChangeThreshold - relative change that triggers session
	VelocityChangeThreshold float64

	// MinSessionDurationSeconds - minimum duration to be a valid session
	MinSessionDurationSeconds float64

	// MaxSessionDurationSeconds - maximum session duration (force break)
	MaxSessionDurationSeconds float64

	// FilterConfig for velocity tracking
	FilterConfig filter.VelocityConfig
}

SessionDetectorConfig holds configuration for session detection.

func DefaultSessionDetectorConfig

func DefaultSessionDetectorConfig() SessionDetectorConfig

DefaultSessionDetectorConfig returns sensible defaults.

type SessionEvent

type SessionEvent struct {
	Type      SessionEventType
	Timestamp time.Time
	NodeID    string
	OldRate   float64
	NewRate   float64
	Reason    string
}

SessionEvent represents a session boundary event.

type SessionEventType

type SessionEventType string

SessionEventType represents types of session events.

const (
	SessionStart    SessionEventType = "start"
	SessionEnd      SessionEventType = "end"
	SessionContinue SessionEventType = "continue"
)

type Tracker

type Tracker struct {
	// contains filtered or unexported fields
}

Tracker is the main temporal tracking system.

func NewTracker

func NewTracker(cfg Config) *Tracker

NewTracker creates a new temporal tracker with the given configuration.

The tracker monitors node access patterns over time and uses Kalman filtering to smooth noisy data and predict future accesses. It automatically detects session boundaries and cyclic patterns.

Parameters:

  • cfg: Configuration for tracking behavior (use DefaultConfig() for defaults)

Returns:

  • *Tracker ready to record accesses and make predictions

Example 1 - Basic Access Tracking:

tracker := temporal.NewTracker(temporal.DefaultConfig())

// Simulate user accessing documents over time
for i := 0; i < 10; i++ {
	tracker.RecordAccess("doc-123")
	time.Sleep(5 * time.Second)
}

// Get statistics
stats := tracker.GetNodeStats("doc-123")
fmt.Printf("Accessed %d times, avg %.1f seconds between accesses\n",
	stats.AccessCount, stats.AverageIntervalSeconds)

Example 2 - Predicting Next Access:

tracker := temporal.NewTracker(temporal.DefaultConfig())

// User accesses a file regularly
for i := 0; i < 5; i++ {
	tracker.RecordAccess("project-file")
	time.Sleep(1 * time.Hour) // Every hour
}

// Predict when they'll access it next
prediction := tracker.PredictNextAccess("project-file")
if prediction != nil {
	fmt.Printf("Likely to access in %.0f minutes\n", prediction.SecondsUntil/60)
	// Output: "Likely to access in 60 minutes"
}

Example 3 - Session Boundary Detection:

tracker := temporal.NewTracker(temporal.DefaultConfig())

// User is actively working
tracker.RecordAccess("doc-1")
time.Sleep(30 * time.Second)
tracker.RecordAccess("doc-1")
time.Sleep(30 * time.Second)

// Long gap - user left for lunch
time.Sleep(2 * time.Hour)

// Check if session changed
if tracker.IsSessionBoundary("doc-1") {
	fmt.Println("New session detected - user returned!")
	// Clear short-term context, log session end, etc.
}

Example 4 - Integration with Decay System:

tracker := temporal.NewTracker(temporal.DefaultConfig())
decayManager := decay.New(nil)

// Track accesses and update decay
onAccess := func(nodeID string) {
	tracker.RecordAccess(nodeID)

	// Predict if node will be accessed soon
	pred := tracker.PredictNextAccess(nodeID)
	if pred != nil && pred.SecondsUntil < 3600 { // Within 1 hour
		// Slow decay for nodes that will be accessed soon
		memory.ImportanceWeight = 0.9
	}
}

ELI12:

Think of NewTracker like starting a stopwatch collection for tracking when your friends visit:

  • Every time Sarah visits, you click her stopwatch: RECORD ACCESS
  • After a few visits, you notice "Sarah comes every day around 3pm"
  • The tracker can predict: "Sarah will probably visit tomorrow at 3pm"
  • If she doesn't visit for a week, it notices: "Session ended, she forgot about us"

The Kalman filter is the "smart brain" that:

  1. Notices patterns (daily visits, weekly visits, etc.)
  2. Handles noise (if she comes at 3:05pm once, don't panic)
  3. Detects trends (is she visiting MORE often or LESS often?)
  4. Makes predictions (when will she visit next?)

Real-world Uses:

  • Cache management: "This file will be accessed in 5 minutes, keep it warm!"
  • Memory decay: "This hasn't been accessed in 2 weeks, archive it"
  • Context switching: "User moved from coding to meetings (30 min gap)"
  • Predictive loading: "User opens Report.docx every Monday at 9am"

Performance:

  • RecordAccess: O(1) - very fast
  • PredictNextAccess: O(1) - simple calculation
  • Memory: ~500 bytes per tracked node
  • MaxTrackedNodes uses LRU eviction (least recently used gets removed)

Thread Safety:

All methods are thread-safe for concurrent access from multiple goroutines.

func (*Tracker) GetAccessRateTrend

func (t *Tracker) GetAccessRateTrend(nodeID string) (velocity float64, trend string)

GetAccessRateTrend returns the access rate trend for a node.

func (*Tracker) GetColdNodes

func (t *Tracker) GetColdNodes(limit int) []string

GetColdNodes returns nodes with decreasing access rates.

func (*Tracker) GetGlobalStats

func (t *Tracker) GetGlobalStats() GlobalStats

GetGlobalStats returns global statistics.

func (*Tracker) GetHotNodes

func (t *Tracker) GetHotNodes(limit int) []string

GetHotNodes returns nodes with increasing access rates.

func (*Tracker) GetStats

func (t *Tracker) GetStats(nodeID string) *NodeStats

GetStats returns statistics for a node.

func (*Tracker) IsSessionBoundary

func (t *Tracker) IsSessionBoundary(nodeID string) bool

IsSessionBoundary checks if a significant session change occurred for a node.

func (*Tracker) PredictNextAccess

func (t *Tracker) PredictNextAccess(nodeID string) *Prediction

PredictNextAccess predicts when a node will be accessed next.

func (*Tracker) RecordAccess

func (t *Tracker) RecordAccess(nodeID string)

RecordAccess records an access to a node at the current time.

This is the primary method for feeding access events into the temporal tracker. Each call updates the node's access statistics, smooths the data with Kalman filtering, and checks for session boundaries.

Parameters:

  • nodeID: Unique identifier for the node being accessed

Example 1 - Simple Access Tracking:

tracker := temporal.NewTracker(temporal.DefaultConfig())

// Record every time user opens a document
tracker.RecordAccess("doc-readme")
tracker.RecordAccess("doc-api")
tracker.RecordAccess("doc-readme") // Accessed again

// Tracker now knows doc-readme is accessed more frequently

Example 2 - Integration with Storage Engine:

tracker := temporal.NewTracker(temporal.DefaultConfig())
engine := storage.NewBadgerEngine("./data")

// Hook into node retrieval
originalGet := engine.GetNode
engine.GetNode = func(id storage.NodeID) (*storage.Node, error) {
	tracker.RecordAccess(string(id)) // Track access
	return originalGet(id)
}

Example 3 - Real-time Recommendation System:

tracker := temporal.NewTracker(temporal.DefaultConfig())

func handleUserAction(userID, itemID string) {
	tracker.RecordAccess(itemID)

	// Get all recently accessed items
	recentItems := tracker.GetRecentlyAccessed(10)

	// Recommend similar items
	recommendations := findSimilarItems(recentItems)
	showRecommendations(userID, recommendations)
}

ELI12:

Think of RecordAccess like clicking a button on your stopwatch app:

  • Click! = "I just used this thing"
  • The app remembers: "Oh, you use this every hour"
  • Next time, it can guess: "You'll probably use it again in 1 hour"

It's like your phone learning you check Instagram every morning at 7am, so it preloads it for you!

Performance:

  • O(1) constant time operation
  • Thread-safe with mutex protection
  • Automatic LRU eviction when MaxTrackedNodes exceeded

Thread Safety:

Safe to call concurrently from multiple goroutines.

func (*Tracker) RecordAccessAt

func (t *Tracker) RecordAccessAt(nodeID string, timestamp time.Time)

RecordAccessAt records an access at a specific time.

func (*Tracker) Reset

func (t *Tracker) Reset()

Reset clears all tracking data.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL