Skip to content

Feature Request: Optimized Batch Feature Evaluation with Shared UserContext for Memory Efficiency #184

@shubham-300

Description

@shubham-300

Requesting a method to evaluate multiple features with a single shared UserContext to reduce memory usage when evaluating many features for the same user.

Problem Statement

When evaluating multiple features for the same user, we create a UserContext once and reuse it across evaluations. However, each call to evalFeature() or getFeatureValue() may create internal copies or duplicate data structures, leading to:

  • Higher memory usage when evaluating 50-100+ features per request
  • Unnecessary object allocations
  • Potential performance overhead from repeated context processing

Use Case

We have a high-throughput service that evaluates 50-100 features per user request. Currently, we:

// Create UserContext once
UserContext userContext = new UserContext.UserContextBuilder()
    .build()
    .witAttributesJson(attributesJson);

// Evaluate multiple features sequentially or in parallel
for (String featureName : featureNames) {
    FeatureResult<Object> result = client.evalFeature(
        featureName, 
        Object.class, 
        userContext  // Same context reused
    );
    // Process result...
}

While UserContext is immutable and thread-safe, the SDK may still create internal copies or process the context multiple times.

Current Behavior

  • Each evalFeature() call processes the UserContext independently
  • No explicit optimization for batch evaluation scenarios
  • Memory usage scales linearly with the number of features evaluated

Proposed Solution

Add a batch evaluation method that:

  1. Accepts a list of feature keys and a single UserContext
  2. Internally optimizes context processing (parse attributes once, reuse parsed data)
  3. Returns a map of feature keys to their evaluated results
  4. Minimizes memory allocations by sharing internal data structures

Proposed API:

// Option 1: New method on GrowthBookClient
Map<String, FeatureResult<Object>> evalFeatures(
    List<String> featureKeys,
    Class<Object> valueTypeClass,
    UserContext userContext
);

// Option 2: Builder pattern for batch operations
BatchEvaluator batchEvaluator = client.createBatchEvaluator(userContext);
Map<String, FeatureResult<Object>> results = batchEvaluator
    .evalFeatures(featureKeys, Object.class);

Benefits

  1. Memory efficiency: parse and process UserContext once for multiple features
  2. Performance: reduce redundant context processing
  3. API clarity: explicit batch evaluation method
  4. Backward compatible: existing single-feature methods remain unchanged

Example Implementation (Pseudo-code)

public Map<String, FeatureResult<ValueType>> evalFeatures(
    List<String> featureKeys,
    Class<ValueType> valueTypeClass,
    UserContext userContext
) {
    // Parse attributes once
    Map<String, Object> parsedAttributes = parseAttributes(userContext.getAttributesJson());
    
    // Pre-compute common context data
    EvaluationContext evalContext = buildEvaluationContext(userContext, parsedAttributes);
    
    // Evaluate all features using shared context
    Map<String, FeatureResult<ValueType>> results = new HashMap<>();
    for (String featureKey : featureKeys) {
        results.put(featureKey, evaluateFeature(featureKey, valueTypeClass, evalContext));
    }
    
    return results;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions