Agent skill

working-with-resources

Work with Resource datasets (mutable state tracking) using OPAL temporal joins. Use when you need to enrich Events/Intervals with contextual state information, track resource state changes over time, or navigate between datasets using temporal relationships. Covers temporal join mechanics (lookup, join, follow), automatic field matching, and when to use Resources vs Reference Tables.

Stars 1
Forks 1

Install this agent skill to your Project

npx add-skill https://github.com/rustomax/observe-community-mcp/tree/main/skills/working-with-resources

SKILL.md

Working with Resources

Work with Resource datasets (mutable state tracking) using OPAL temporal joins. Resources track state changes over time with Valid From/To timestamps. Resources are rarely useful by themselves - their power comes from enriching Events/Intervals with contextual state information through temporal joins.

Use when you need to:

  • Enrich spans/logs with service metadata (type, environment, namespace)
  • Track database dependencies between services
  • Add resource state context to point-in-time events
  • Navigate from events to related resources across datasets

Covers temporal join mechanics, three working join verbs (lookup, join, follow), and automatic field matching.

Key Concepts

What Are Resources?

Resources represent mutable state that changes over time:

  • Valid From / Valid To timestamps (nanoseconds)
  • Track how resource properties evolve (e.g., service environment changes)
  • Created from Events/Intervals using make_resource verb
  • Different from Intervals (which are immutable completed activities)

Example: OpenTelemetry/Service Resource tracks service metadata:

service_name: "checkoutservice"
service_type: "Service"
environment: "eu01"
Valid From: 2025-01-10T00:00:00Z
Valid To: 2025-01-11T00:00:00Z

Temporal Joins

Critical: Resources use automatic temporal joins based on timestamp overlap:

Span:     |-----------|  (start_time to end_time)
Resource:    |-------------|  (Valid From to Valid To)
Match: YES (overlap exists)

OPAL automatically:

  1. Matches timestamps (span time must overlap Resource validity period)
  2. Matches field names (e.g., service_name = service_name)
  3. No explicit ON clause needed

Performance note: join is faster than lookup for Resource joins (avoids extra left outer join pass).

Join Verbs for Resources

Three join verbs work reliably with Resources:

Verb Returns Performance Use Case
lookup All left rows + Resource fields Slower (two-pass) Enrichment - keep all data
join Only matching rows + Resource fields Faster (single-pass) Filter + enrichment
follow Right dataset rows that match Unknown Cross-dataset navigation

Pattern 1: lookup - Enrich All Rows

Use when: You want to keep ALL primary dataset rows, with optional Resource enrichment.

opal
# Enrich ALL checkoutservice spans with service metadata
filter service_name = "checkoutservice"
| lookup @service_resource
| make_col svc:service_name,
          svc_type:service_type,
          svc_env:environment,
          dur_ms:duration / 1ms
| topk 10

Behavior:

  • Returns all spans (even if no Resource match found)
  • Resource fields populated when match exists
  • Slower due to two-pass join (band-join + left outer join)

Common MCP parameters:

json
{
  "primary_dataset_id": "42160967",
  "secondary_dataset_ids": ["42160979"],
  "dataset_aliases": {"service_resource": "42160979"}
}

Pattern 2: join - Filter to Matches Only

Use when: You only want rows WITH Resource matches (faster performance).

opal
# Show only spans where service metadata exists
filter service_name = "checkoutservice"
| join @service_resource
| make_col svc:service_name,
          svc_type:service_type,
          dur_ms:duration / 1ms
| limit 10

Behavior:

  • Returns only spans with matching Resources
  • Faster than lookup (single-pass band-join)
  • Filters out spans with no Resource match

When to use:

  • Performance matters
  • You don't need rows without Resource context
  • Filtering to matches is acceptable

Pattern 3: follow - Navigate to Related Dataset

Use when: You want to find related dataset rows (cross-dataset navigation).

Critical difference: follow returns rows from the FOLLOWED dataset, not the primary dataset!

opal
# Given error logs, show ALL related spans by namespace
filter body ~ /error/i
| follow @spans.service_namespace = namespace
| make_col svc:service_name,
          span:span_name,
          dur_ms:duration / 1000000
| limit 20

Syntax:

follow @dataset.field = primary_field

Behavior:

  • Returns rows from @spans (not from logs)
  • Matches based on field equality + temporal overlap
  • Use case: "Given these logs, show me all related spans"

Important: This is NOT enrichment - you're switching to a different dataset entirely.

Common MCP parameters for follow:

json
{
  "primary_dataset_id": "42161740",
  "secondary_dataset_ids": ["42160967"],
  "dataset_aliases": {"spans": "42160967"}
}

Common Patterns

Pattern: Service Inventory from Resource

opal
# Count services by type in eu01 environment
filter environment = "eu01"
| make_col svc:service_name,
          svc_type:service_type,
          svc_ns:service_namespace
| statsby count:count(), group_by(svc_type)
| sort desc(count)

Result: 14 Services + 1 Database in eu01

Note: This queries the Resource dataset directly (no join needed).

Pattern: Database Dependencies

opal
# Find which services call which databases
make_col caller:parent_service_name,
         database:service_name,
         env:environment
| statsby count:count(), group_by(caller, database, env)
| sort desc(count)
| topk 10

Dataset: ServiceExplorer/Database Call (Resource)

Result: Shows cartservice → redis, observe-community-mcp → postgresql

Pattern: Enrich Spans with Service Context

opal
# Add service type and environment to high-latency spans
filter duration > 100ms
| join @service_resource
| make_col svc:service_name,
          type:service_type,
          env:environment,
          latency_ms:duration / 1ms
| statsby avg_latency:avg(latency_ms),
          count:count(),
          group_by(svc, type, env)
| sort desc(avg_latency)

Why join here: Filtering to spans with service metadata is acceptable, and it's faster than lookup.

Pattern: Navigate from Logs to Spans

opal
# Find all spans from namespaces that logged errors
filter body ~ /error/i
| follow @spans.service_namespace = namespace
| make_col svc:service_name,
          span:span_name,
          dur_ms:duration / 1000000
| statsby error_span_count:count(),
          avg_latency:avg(dur_ms),
          group_by(svc, span)
| sort desc(error_span_count)

Why follow: We want to see ALL spans from namespaces with errors, not just enrich the error logs.

Dataset: Kubernetes Logs (42161740) following to OpenTelemetry/Span (42160967)

Troubleshooting

Issue: "Field not found" after join

Cause: Resource field name doesn't match primary dataset field.

Solution: Check exact field names in Resource dataset:

discover_context(dataset_id="42160979")

Copy field names exactly (case-sensitive!).

Issue: No Resource matches found

Diagnosis:

  1. Check temporal overlap - Resource must be valid during span/event time
  2. Verify field name matching (e.g., both have service_name)
  3. Confirm Resource data exists for that time range

Solution: Query Resource dataset directly to see available data:

opal
filter service_name = "checkoutservice"
| make_col valid_from:int64("Valid From"),
          valid_to:int64("Valid To")
| limit 5

Issue: "implicit lookup does not support additional arguments"

Error: Tried using ON clause with lookup:

opal
| lookup @resource on service_name = @resource.service_name  ❌

Fix: Remove ON clause - temporal joins are automatic:

opal
| lookup @resource  ✅

Issue: follow returns unexpected dataset

Cause: follow returns rows from the FOLLOWED dataset, not primary.

This is correct behavior:

  • lookup/join: Keep primary dataset structure + add Resource fields
  • follow: Switch to followed dataset, return matching rows

Solution: Use lookup or join if you want to enrich primary dataset.

Available Resource Datasets

Common Resources in Observe (from discovery):

OpenTelemetry/Service (ID: 42160979):

  • service_name (str)
  • service_type (str) - "Database", "Service"
  • environment (str)
  • service_namespace (str)

ServiceExplorer/Database Call (ID: 42160978):

  • service_name (str) - Database name
  • parent_service_name (str) - Calling service
  • environment (str)

Discovery: Use MCP to find Resources:

discover_context("resource", result_type="dataset", interface_filter="resource")

Performance Considerations

Faster: join over lookup

  • Documentation recommends join for Resource/Interval joins
  • Avoids extra left outer join pass
  • Only use lookup when you MUST keep all rows

Temporal join cost:

  • OPAL performs interval overlapping band-join
  • Matches timestamps + field names automatically
  • More expensive than simple equality joins

Best practice: Filter primary dataset first to reduce join volume:

opal
filter service_name = "checkoutservice"  # Filter FIRST
| join @service_resource                 # Then join

Key Takeaways

  1. Resources track mutable state over time (Valid From/To timestamps)
  2. Power comes from temporal joins, not standalone queries
  3. Three working join verbs:
    • lookup - enrich all rows (slower)
    • join - filter to matches (faster)
    • follow - navigate to related dataset
  4. Automatic temporal matching - no ON clause needed
  5. Performance: Prefer join over lookup when filtering acceptable

References

  • Temporal joins use interval overlapping band-join
  • set_valid_from and set_valid_to define Resource validity
  • join is faster than lookup (single-pass vs two-pass)
  • follow requires field-based join condition: follow @dataset.field = primary_field

Expand your agent's capabilities with these related and highly-rated skills.

rustomax/observe-community-mcp

working-with-reference-tables

Work with Reference Tables (static CSV lookup data) using OPAL to enrich datasets with descriptive information. Use when you need to map IDs to human-readable names, add static metadata from CSV uploads, or perform lookups without temporal considerations. Covers both explicit and implicit lookup patterns, column name matching, and when to choose Reference Tables vs Resources vs Correlation Tags.

1 1
Explore
rustomax/observe-community-mcp

analyzing-text-patterns

Extract and analyze recurring patterns from log messages, span names, and event names using punctuation-based template discovery. Use when you need to understand log diversity, identify common message structures, detect unusual formats, or prepare for log parser development. Works by removing variable content and preserving structural markers.

1 1
Explore
rustomax/observe-community-mcp

time-series-analysis

Analyze event datasets (logs) and intervals over time using OPAL timechart. Use when you need to visualize trends, track metrics over time, or create time-series charts. Covers timechart for temporal binning, bin duration options (1h, 5m, 1d), options(bins:N) for controlling bin count, and understanding temporal output columns (_c_valid_from, _c_valid_to, _c_bucket). Returns multiple rows per group for time-series visualization. For single summaries, see aggregating-event-datasets skill.

1 1
Explore
rustomax/observe-community-mcp

detecting-anomalies

Detect anomalies in metrics and time-series data using OPAL statistical methods. Use when you need to identify unusual patterns, spikes, drops, or outliers in observability data. Covers statistical outlier detection (Z-score, IQR), threshold-based alerts, rate-of-change detection with window functions, and moving average baselines. Choose pattern based on data distribution and anomaly type.

1 1
Explore
rustomax/observe-community-mcp

aggregating-gauge-metrics

Aggregate pre-computed metrics (gauge, counter, delta types) using OPAL. Use when analyzing request counts, error rates, resource utilization, or any numeric metrics over time. Covers align + m() + aggregate pattern, summary vs time-series output, and common aggregation functions. For percentile metrics (tdigest), see analyzing-tdigest-metrics skill.

1 1
Explore
rustomax/observe-community-mcp

aggregating-event-datasets

Aggregate and summarize event datasets (logs) using OPAL statsby. Use when you need to count, sum, or calculate statistics across log events. Covers make_col for derived columns, statsby for aggregation, group_by for grouping, aggregation functions (count, sum, avg, percentile), and topk for top N results. Returns single summary row per group across entire time range. For time-series trends, see time-series-analysis skill.

1 1
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results