Skip to main content

External App Cache Optimization

Overview

External Nextcloud apps calling ObjectService.searchObjectsPaginated() can experience slow response times due to cache thrashing. This guide explains how to optimize caching for external apps accessing OpenRegister programmatically.

The Problem

Cache Thrashing for External Apps

Issue: All external apps without user sessions share the same 'anonymous' cache namespace, causing:

  • Cache invalidation conflicts between different apps
  • Poor cache hit rates due to mixed usage patterns
  • Slow responses (2+ seconds instead of <500ms)
  • Resource waste from repeated identical queries

The Solution

Enhanced Cache Isolation

Implementation Guide

Best Performance: Explicitly set your app ID for optimal cache isolation.

<?php
// Get ObjectService from dependency injection
$objectService = \OC::$server->get(\OCA\OpenRegister\Service\ObjectService::class);

// **CRITICAL**: Set your app context for cache optimization
$objectService->setExternalAppContext('myapp');

// Now use ObjectService with optimized caching
$query = [
'@self' => ['register' => 19, 'schema' => 116],
'_limit' => 50,
'_search' => 'test query'
];

$results = $objectService->searchObjectsPaginated($query);

Method 2: Automatic Detection (Fallback)

Good Performance: The system automatically detects your app from the call stack.

<?php
// System automatically detects calling app from file path
// /apps/myapp/lib/Service/DataService.php -> 'myapp'
// /apps-extra/customapp/lib/Controller/ApiController.php -> 'customapp'

$objectService = \OC::$server->get(\OCA\OpenRegister\Service\ObjectService::class);
$results = $objectService->searchObjectsPaginated($query);
// Cache namespace: 'external_app_myapp' (auto-detected)

Method 3: Query Fingerprinting (Last Resort)

Basic Performance: For undetectable apps, uses query patterns for isolation.

<?php
// When app cannot be detected, system creates query-based cache isolation
$results = $objectService->searchObjectsPaginated($query);
// Cache namespace: 'anonymous_a1b2c3d4' (based on query pattern)

Performance Impact

Before Optimization

ScenarioCache Hit RateResponse TimeIssue
Multiple external apps~10%2-5 secondsCache thrashing
Same app, repeated calls~20%1-3 secondsShared anonymous cache
Different query patterns~5%3+ secondsPattern conflicts

After Optimization

ScenarioCache Hit RateResponse TimeImprovement
App with setExternalAppContext()~95%<500ms🚀 10x faster
Auto-detected external app~80%<750ms🚀 4x faster
Query fingerprint isolation~60%<1s🚀 3x faster

Cache Key Structure

Enhanced Cache Key Generation

// Cache key components
$keyData = [
'query' => $query, // Actual query parameters
'user' => $effectiveUserId, // Enhanced user context
'org' => $orgId, // Organization context
'version' => '1.1' // Cache version
];

// Effective user contexts:
// - Regular users: 'user123'
// - External apps: 'external_app_myapp'
// - Anonymous patterns: 'anonymous_a1b2c3d4'

Cache Isolation Benefits

Best Practices

For External App Developers

  1. Always set app context before making ObjectService calls:

    $objectService->setExternalAppContext('your_app_name');
  2. Reuse ObjectService instances to maintain app context:

    class MyService {
    private ObjectService $objectService;

    public function __construct() {
    $this->objectService = \OC::$server->get(ObjectService::class);
    $this->objectService->setExternalAppContext('myapp');
    }
    }
  3. Use consistent app IDs across your application:

    // Good: Consistent app ID
    $objectService->setExternalAppContext('myapp');

    // Bad: Different IDs fragment cache
    $objectService->setExternalAppContext('myapp_v1');
    $objectService->setExternalAppContext('myapp_production');

For OpenRegister Integration

  1. Check cache performance in logs:

    docker logs master-nextcloud-1 | grep 'External app cache context'
  2. Monitor cache hit rates:

    docker logs master-nextcloud-1 | grep 'Cache hit - returning cached response'
  3. Verify app detection:

    docker logs master-nextcloud-1 | grep 'External app cache context determined'

Cache Invalidation Coordination

Automatic Invalidation

Cache invalidation works seamlessly for external apps:

Cache Scope

OperationInvalidation ScopeExternal App Impact
Create/Update ObjectRegister + Schema specific✅ Affected apps only
Delete ObjectRegister + Schema specific✅ Affected apps only
Schema ChangesAll related caches✅ All apps using schema
Bulk OperationsGlobal or context-specific✅ Appropriate scope

Performance Monitoring

Debug Logging

Enable cache monitoring for external apps:

// Check if your app context is being detected
$this->logger->debug('Starting ObjectService call', [
'app' => 'myapp',
'query' => $query
]);

$objectService->setExternalAppContext('myapp');
$results = $objectService->searchObjectsPaginated($query);

Log Analysis

# Check cache performance for your app
docker logs master-nextcloud-1 | grep 'external_app_myapp'

# Monitor cache hit rates
docker logs master-nextcloud-1 | grep 'Cache hit.*myapp'

# Check for cache invalidation events
docker logs master-nextcloud-1 | grep 'cache invalidation.*completed'

Migration Guide

Updating Existing External Apps

Before (slow performance):

$objectService = \OC::$server->get(ObjectService::class);
$results = $objectService->searchObjectsPaginated($query); // Shared 'anonymous' cache

After (optimized performance):

$objectService = \OC::$server->get(ObjectService::class);
$objectService->setExternalAppContext('myapp'); // Isolated cache namespace
$results = $objectService->searchObjectsPaginated($query); // App-specific caching

Expected Performance Gains

App Usage PatternBeforeAfterImprovement
High-frequency identical queries2-3s<100ms🚀 20-30x faster
Similar query patterns1-2s<300ms🚀 5-7x faster
Mixed query patterns2-5s<500ms🚀 4-10x faster

Troubleshooting

Common Issues

Issue: Still getting slow responses after setting app context Solution: Verify app context is set before each ObjectService call

Issue: Cache hits not showing in logs
Solution: Enable debug logging and check for cache invalidation conflicts

Issue: Different response times for identical queries Solution: Ensure consistent app ID usage and check for cache invalidation timing

Verification Steps

  1. Check app context detection:

    docker logs master-nextcloud-1 | grep 'External app cache context determined'
  2. Verify cache isolation:

    docker logs master-nextcloud-1 | grep 'external_app_yourappname'
  3. Monitor response times:

    docker logs master-nextcloud-1 | grep 'searchObjectsPaginated.*completed'

Related Documentation: