flyteorg / flyteorg/flyte

Dynamic workflow recompiles every round when compiled CRD blob exceeds storage-cache entry cap: getRawBytes discards valid data on CACHE_WRITE_FAILED

Open Beginner friendly
#7,916 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
7.5k
Forks
886
Avg merge
1d 14h
Merged PRs (30d)
120

Description

## Describe the bug

When a dynamic node's compiled-workflow cache blob is larger than the storage cache's
per-entry limit, flytepropeller recompiles the dynamic workflow on **every** evaluation
round, even though the blob is read back from blob storage successfully each time. On a
large dynamic fan-out this recompilation dominates round time and node admission slows to
a crawl; the workflow can effectively stall while showing RUNNING.

Log signature (one pair per evaluation round):

```
Failed to load cached flyte workflow, this will cause the dynamic workflow to be
recompiled. Error: [CACHE_WRITE_FAILED] Failed to Cache the metadata, caused by:
The entry size is larger than 1/1024 of cache size
```

## Root cause

`flytestdlib/storage/cached_rawstore.go` `ReadRaw` returns the successfully-read data
**together with a non-nil error** when the read succeeded but the freecache `Set` failed
(freecache rejects entries larger than 1/1024 of total cache size, so any blob over
`storage.cache.max_size_mbs / 1024` MB fails to memoize on every read).

flytestdlib PR flyteorg/flytestdlib#28 (2019) introduced `ErrFailedToWriteCache` /
`IsFailedWriteToCache` precisely so callers can treat this as a soft failure, and
`ReadProtobuf` in `protobuf_store.go` does check it and proceeds with the fetched bytes.

But the two sibling reads in
`flytepropeller/pkg/controller/nodes/task/remote_workflow_store.go` are asymmetric:

- `GetCompiledWorkflow` → `ReadProtobuf` → handles `IsFailedWriteToCache` correctly.
- `GetWorkflowCRD` → local `getRawBytes` → calls `store.ReadRaw` directly and does
`if err != nil { return nil, err }`, discarding the valid data.

So `RetrieveCache` fails, `dynamic_workflow.go` logs the warning above, increments the
`CacheError` metric, and recompiles — every round, forever, for any dynamic workflow whose
CRD JSON blob exceeds the entry cap. The `metastore:cache_write_err` counter climbs in
lockstep (we observed tens of thousands over one workflow execution).

Still present on master (`remote_workflow_store.go` and `cached_rawstore.go` unchanged);
observed in production on v1.15.3.

## Expected behavior

A cache-*write* failure after a successful backing-store read shouldn't fail the read.
`getRawBytes` should mirror the existing `ReadProtobuf` pattern:

```go
rawReader, err := r.store.ReadRaw(ctx, source)
if err != nil && !storage.IsFailedWriteToCache(err) {
return nil, err
}
```

(plus nil-guard on the reader), so oversized blobs cost one blob-store GET per round
instead of a full recompile.

## Steps to reproduce

1. Configure `storage.cache.max_size_mbs: 1024` (entry cap becomes 1 MB).
2. Run a dynamic workflow large enough that its compiled CRD JSON (`futures_compiled.pb`)
exceeds 1 MB — a wide ArrayNode fan-out gets there quickly.
3. Watch propeller logs: the CACHE_WRITE_FAILED warning pair repeats every round and the
dynamic workflow recompiles each time; `cache_write_err` grows unboundedly.

## Context

- flyte v1.15.3 (flyte-core helm chart), storage type s3.
- Related history: flyteorg/flytepropeller#254 introduced the compiled-workflow cache
split where the asymmetry lives; flyteorg/flytestdlib#28 introduced the soft-failure
contract that `getRawBytes` misses; #4611 discusses the cache mechanism.

Contributor guide

Open the contributing guide

Research direction

Start in flytepropeller/pkg/controller/nodes/task/remote_workflow_store.go at getRawBytes, then compare its ReadRaw handling with ReadProtobuf in protobuf_store.go. Read flytestdlib/storage/cached_rawstore.go to confirm the soft-failure contract. Done means a successful backing-store read is retained when cache writing fails, so oversized CRD blobs do not trigger repeated recompilation.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.