Skip to content

🔥feat: Add storage backed SharedState for prefork applications#4243

Merged
ReneWerner87 merged 10 commits into
mainfrom
implement-shared-state-component-using-fiber.storage
May 3, 2026
Merged

🔥feat: Add storage backed SharedState for prefork applications#4243
ReneWerner87 merged 10 commits into
mainfrom
implement-shared-state-component-using-fiber.storage

Conversation

@gaby
Copy link
Copy Markdown
Member

@gaby gaby commented Apr 28, 2026

Motivation

  • Provide a storage-backed, prefork-safe key–value state store for data that must be shared across workers or processes.
  • Allow apps to namespace shared keys to avoid collisions (with sensible default prefixing that can include AppName) and reuse app-configured encoders/decoders for JSON/MsgPack/CBOR/XML.

Description

  • Add SharedState implementation (shared_state.go) exposing raw and encoded helpers such as Set, Get, SetJSON, GetJSON, SetMsgPack, GetMsgPack, SetCBOR, GetCBOR, SetXML, GetXML, Delete, Has, and context-aware variants.
  • Wire SharedState into the app by adding Config.SharedStorage and Config.SharedStatePrefix, initializing app.sharedState in New(...), and exposing App.SharedState() while clarifying that App.State() remains process-local.
  • Implement predictable key namespacing (defaultSharedStatePrefix + optional AppName) and ensure the SharedState uses the app-provided encoder/decoder hooks.
  • Update documentation (docs/api/app.md, docs/api/state.md, docs/whats_new.md) and add comprehensive unit tests in shared_state_test.go covering configuration, context propagation, serialization, codec usage, namespacing, and error propagation.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 28, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a storage-backed, prefork-safe SharedState to App with Config options (SharedStorage, SharedStatePrefix); exposes App.SharedState(); implements byte-level and codec-aware (JSON/MsgPack/CBOR/XML) Set/Get/Delete/Has/Reset/Close operations with context variants; initializes and closes shared storage during app lifecycle; includes docs and tests.

Changes

Cohort / File(s) Summary
Core App surface
app.go
Adds Config.SharedStorage, Config.SharedStatePrefix, App.sharedState field; initializes shared state in New() via newSharedState(&app.config); exposes func (app *App) SharedState() *SharedState; closes storage in post-shutdown hook with error logging.
SharedState implementation
shared_state.go
New exported SharedState type and constructor newSharedState; exported error ErrSharedStorageNotConfigured; byte-level APIs (Set/Get/Delete/Has) with context variants, TTL support, defensive-copy semantics, empty-key behavior, Reset/Close; codec-aware helpers for JSON/MsgPack/CBOR/XML with panic recovery and configurable encoders/decoders; key prefixing via SharedStatePrefix or default including AppName.
Tests — SharedState
shared_state_test.go
Adds comprehensive tests covering missing-storage behavior, nil-receiver handling, context-variant enforcement, codec hook invocation and error paths, prefork/shared-name isolation, prefix defaults, defensive-copy verification, TTL/delete/has semantics, and simulated storage errors.
Tests — Shutdown hooks
services_test.go
Moves stringsLogger to file scope; adds shutdownHookStorage mock and subtests asserting SharedStorage.Close() is invoked during post-shutdown, and that close errors are logged alongside service shutdown errors.
Docs
docs/api/app.md, docs/api/state.md, docs/whats_new.md
Documents State() vs SharedState() distinction; adds configuration notes (SharedStorage, SharedStatePrefix), API surface (WithContext variants, codec helpers), usage examples, and whats-new entry.

Sequence Diagram(s)

sequenceDiagram
    participant App as App
    participant SS as SharedState
    participant St as Storage
    rect rgba(200,230,255,0.5)
    App->>SS: SharedState().SetJSON(key, value, ttl)
    SS->>SS: encode JSON (configured encoder)
    SS->>St: Set(prefixedKey, bytes, ttl)
    St-->>SS: ok / error
    SS-->>App: return error or nil
    end

    rect rgba(230,255,200,0.5)
    App->>SS: SharedState().GetJSON(key)
    SS->>St: Get(prefixedKey)
    St-->>SS: bytes / not-found / error
    SS->>SS: decode JSON (configured decoder)
    SS-->>App: decoded value, found flag, error
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • sixcolors
  • efectn
  • ReneWerner87

Poem

🐇 Hopping bytes beneath moon's gate,
I stash our keys to share the state,
JSON, CBOR, MsgPack cheer,
Prefork-safe — no race to fear,
Close the burrow when it's late.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title uses emoji and clearly identifies the primary change as adding storage-backed SharedState for prefork applications, though the emoji may be noise.
Description check ✅ Passed Description provides clear motivation, implementation details, and covers most key aspects including documentation and testing, though some checklist items are not explicitly addressed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch implement-shared-state-component-using-fiber.storage

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ReneWerner87 ReneWerner87 added this to v3 Apr 28, 2026
@ReneWerner87 ReneWerner87 added this to the v3 milestone Apr 28, 2026
@gaby gaby changed the title Introduce SharedState: storage-backed, prefork-safe app state with codecs and tests 🔥feat: Add storage backed SharedState for prefork applications Apr 28, 2026
@gaby gaby moved this to In Progress in v3 Apr 28, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 28, 2026

Codecov Report

❌ Patch coverage is 92.45283% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.25%. Comparing base (fbc03bd) to head (7b016c0).
⚠️ Report is 28 commits behind head on main.

Files with missing lines Patch % Lines
shared_state.go 92.23% 9 Missing and 7 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4243      +/-   ##
==========================================
+ Coverage   91.23%   91.25%   +0.02%     
==========================================
  Files         126      127       +1     
  Lines       12341    12553     +212     
==========================================
+ Hits        11259    11455     +196     
- Misses        678      687       +9     
- Partials      404      411       +7     
Flag Coverage Δ
unittests 91.25% <92.45%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces SharedState, a storage-backed state management system for Fiber designed to be safe for prefork workers and multi-process environments. It adds new configuration fields to Config, implements a variety of data access methods with support for multiple encoding formats (JSON, MsgPack, CBOR, XML), and provides comprehensive documentation and tests. Review feedback suggests removing a redundant string copy in the key generation logic and highlights potential concerns regarding key length and readability when using hex encoding.

Comment thread shared_state.go Outdated
Comment thread shared_state.go Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new storage-backed SharedState API to Fiber apps to support prefork-safe / multi-process shared key–value state, with configurable namespacing and codec helpers aligned with app-configured encoders/decoders.

Changes:

  • Introduces SharedState with raw (Set/Get) and encoded (SetJSON/GetJSON, SetMsgPack/GetMsgPack, SetCBOR/GetCBOR, SetXML/GetXML) operations, plus context-aware variants.
  • Wires shared state into App via Config.SharedStorage and Config.SharedStatePrefix, and exposes App.SharedState() alongside clarified App.State() semantics.
  • Adds documentation and a comprehensive unit test suite covering configuration, namespacing, context propagation, and error handling.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
shared_state.go Implements the SharedState API over fiber.Storage, including prefixing and codec helpers.
app.go Adds shared-state configuration, initializes shared state in New(), and exposes App.SharedState().
shared_state_test.go Adds unit tests for shared-state behavior (storage config, namespacing, codecs, context, error propagation).
docs/api/state.md Documents prefork-safe SharedState, configuration, method signatures, and examples.
docs/api/app.md Updates app API docs to distinguish State() (process-local) vs SharedState() (storage-backed).
docs/whats_new.md Notes SharedState as a new v3 app feature.

Comment thread shared_state.go Outdated
@gaby gaby marked this pull request as ready for review April 28, 2026 13:36
@gaby gaby requested a review from a team as a code owner April 28, 2026 13:36
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.50.

Benchmark suite Current: 7b016c0 Previous: fbc03bd Ratio
Benchmark_Compress/Zstd (github.com/gofiber/fiber/v3/middleware/compress) - B/op 1 B/op 0 B/op +∞

This comment was automatically generated by workflow using github-action-benchmark.

Copy link
Copy Markdown
Member

@ReneWerner87 ReneWerner87 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good feature idea, storage-backed shared state for prefork is a real need. The integration with the existing fiber.Storage interface and app config is clean. A few things to address before merge:


1. Nil encoder panic (Bug - Medium)

Only JSON gets defaults in newSharedState:

if jsonEncoder == nil { jsonEncoder = json.Marshal }
if jsonDecoder == nil { jsonDecoder = json.Unmarshal }

MsgPack, CBOR, and XML encoders have no nil guard. Calling SetMsgPack("key", val, ttl) without configuring Config.MsgPackEncoder will panic with a nil pointer dereference.

Should either return a descriptive error (e.g. ErrMsgPackEncoderNotConfigured) or add nil checks before calling the encoder/decoder in each method.


2. Massive code duplication (Design - Major)

SetJSON/GetJSON, SetMsgPack/GetMsgPack, SetCBOR/GetCBOR, SetXML/GetXML are near-identical — ~200 lines of copy-paste where only the encoder/decoder function and error string differ. This could be reduced to two internal helpers:

func (s *SharedState) setEncoded(ctx context.Context, key string, v any, ttl time.Duration,
    encoder func(any) ([]byte, error), format string) error {
    if s == nil || s.storage == nil {
        return ErrSharedStorageNotConfigured
    }
    if key == "" {
        return nil
    }
    encoded, err := encoder(v)
    if err != nil {
        return fmt.Errorf("fiber: failed to encode shared state %s value: %w", format, err)
    }
    return s.storage.SetWithContext(ctx, s.key(key), encoded, ttl)
}

func (s *SharedState) getEncoded(ctx context.Context, key string, out any,
    decoder func([]byte, any) error, format string) ([]byte, bool, error) {
    if s == nil || s.storage == nil {
        return nil, false, ErrSharedStorageNotConfigured
    }
    data, err := s.storage.GetWithContext(ctx, s.key(key))
    if err != nil {
        return nil, false, err
    }
    if data == nil {
        return nil, false, nil
    }
    if err := decoder(data, out); err != nil {
        return nil, false, fmt.Errorf("fiber: failed to decode shared state %s value: %w", format, err)
    }
    return append([]byte(nil), data...), true, nil
}

Each public method becomes a one-liner:

func (s *SharedState) SetJSONWithContext(ctx context.Context, key string, v any, ttl time.Duration) error {
    return s.setEncoded(ctx, key, v, ttl, s.jsonEncoder, "json")
}

This also makes the nil-encoder check (#1) fixable in a single place.


3. Inconsistent empty-key handling (Bug - Medium)

  • Set("") → calls storage with empty key (key("") returns "", bypassing prefix)
  • SetJSON("") → silently returns nil (guarded)
  • Get("") → calls storage with empty key
  • Delete("") → calls storage with empty key
  • Has("") → calls storage with empty key

The encoded variants guard against empty keys, but raw Set/Get/Delete/Has do not. This should be consistent — either guard in key() itself or in all public methods.

Copilot flagged this too — the key() function returning "" for empty input effectively bypasses the prefix, which is likely unintended.


4. newSharedState takes 10 parameters (Style - Low)

Could take a subset of Config or a small options struct instead:

func newSharedState(storage Storage, prefix string, cfg Config) *SharedState

5. Dead code: double prefix defaulting (Low)

app.go already ensures a non-empty prefix before calling newSharedState:

sharedStatePrefix := app.config.SharedStatePrefix
if sharedStatePrefix == "" {
    sharedStatePrefix = defaultSharedStatePrefix
    // ...
}

The fallback in newSharedState (if prefix == "" { prefix = defaultSharedStatePrefix }) is unreachable when called from New().


6. No Reset()/Close() forwarding (Low)

The Storage interface has Reset() and Close(). SharedState doesn't expose these. If a user needs to clean up shared state or close the storage connection via SharedState, they currently can't. Consider at least a Close() method, or document that the caller manages the storage lifecycle directly.

Copy link
Copy Markdown
Member

@ReneWerner87 ReneWerner87 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline review comments on specific locations.

Comment thread shared_state.go Outdated
Comment thread shared_state.go
Comment thread shared_state.go
Comment thread shared_state.go Outdated
Comment thread shared_state.go
Comment thread shared_state.go
@gaby
Copy link
Copy Markdown
Member Author

gaby commented Apr 30, 2026

@copilot address the comments made on this post #4243 (review)

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app.go`:
- Around line 93-94: The SharedState instance (sharedState) is never closed
during shutdown; call sharedState.Close() when the app is shutting down — either
add a call to sharedState.Close() in ShutdownWithContext or invoke it in the
existing OnPostShutdown hook alongside services shutdown so storage backend
resources are released; reference the sharedState variable and the
SharedState.Close() method and ensure you handle/propagate any returned error
(log or return) consistent with existing shutdown error handling.

In `@shared_state_test.go`:
- Around line 697-734: The test TestSharedState_EmptyKeyBehavior calls
SetMsgPack and SetCBOR but creates the app without MsgPackEncoder/CBOREncoder,
so those calls should return errors per
TestSharedState_UnconfiguredCodecsReturnErrorInsteadOfPanic; fix by either
removing the SetMsgPack/SetCBOR (and corresponding GetMsgPack/GetCBOR)
assertions from this test, or instantiate the app with MsgPackEncoder and
CBOREncoder set in New(Config{...}) so SetMsgPack and SetCBOR succeed—update the
test to use one of these two approaches and adjust expected error/no-error
assertions accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: be2632c8-a481-4929-a958-2dc99cdc8ff8

📥 Commits

Reviewing files that changed from the base of the PR and between a72c503 and c057a7c.

📒 Files selected for processing (5)
  • app.go
  • docs/api/state.md
  • docs/whats_new.md
  • shared_state.go
  • shared_state_test.go
✅ Files skipped from review due to trivial changes (2)
  • docs/whats_new.md
  • docs/api/state.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • shared_state.go

Comment thread app.go
Comment thread shared_state_test.go
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 30, 2026

Good feature idea, storage-backed shared state for prefork is a real need. The integration with the existing fiber.Storage interface and app config is clean. A few things to address before merge:

...

Addressed in a4946e9: the encoded helpers now share common set/get paths, empty keys are handled consistently across raw and encoded methods, newSharedState no longer takes ten separate arguments, Reset/Close are forwarded to the storage, and the related tests/docs were updated.

@copilot address the comments made on this post #4243 (review)

Addressed in a4946e9.

Copilot finished work on behalf of gaby April 30, 2026 04:16
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
app.go (1)

1384-1390: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Close SharedState during shutdown.

OnPostShutdown still only tears down services. The new storage-backed sharedState can keep backend resources open unless it is closed here too.

🔧 Suggested fix
 app.Hooks().OnPostShutdown(func(_ error) error {
+	if app.sharedState != nil && app.config.SharedStorage != nil {
+		if err := app.sharedState.Close(); err != nil {
+			log.Errorf("failed to close shared state: %v", err)
+		}
+	}
 	if err := app.shutdownServices(app.servicesShutdownCtx()); err != nil {
 		log.Errorf("failed to shutdown services: %v", err)
 	}
 	return nil
 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app.go` around lines 1384 - 1390, OnPostShutdown currently only calls
app.shutdownServices; also close the storage-backed shared state there. In the
app.Hooks().OnPostShutdown callback, after calling
app.shutdownServices(app.servicesShutdownCtx()), check if app.sharedState (or
the field that holds the SharedState) is non-nil and call its Close() method,
logging any error (e.g., log.Errorf("failed to close sharedState: %v", err)).
Ensure you perform the Close() call regardless of service shutdown result and
return nil as before.
shared_state_test.go (1)

703-730: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Drop the MsgPack/CBOR empty-key assertions or configure the codecs.

New(Config{SharedStorage: ...}) leaves MsgPack/CBOR unset here, so these SetMsgPack/SetCBOR calls will return the shared-state codec error instead of succeeding.

🔧 Suggested fix
 	require.NoError(t, app.SharedState().Set("", []byte("raw"), time.Minute))
 	require.NoError(t, app.SharedState().SetJSON("", Map{"v": 1}, time.Minute))
-	require.NoError(t, app.SharedState().SetMsgPack("", Map{"v": 1}, time.Minute))
-	require.NoError(t, app.SharedState().SetCBOR("", Map{"v": 1}, time.Minute))
 	require.NoError(t, app.SharedState().SetXML("", Map{"v": 1}, time.Minute))
@@
 	_, found, err = app.SharedState().GetJSON("", &Map{})
 	require.NoError(t, err)
 	require.False(t, found)
-
-	_, found, err = app.SharedState().GetMsgPack("", &Map{})
-	require.NoError(t, err)
-	require.False(t, found)
-
-	_, found, err = app.SharedState().GetCBOR("", &Map{})
-	require.NoError(t, err)
-	require.False(t, found)
 
 	_, found, err = app.SharedState().GetXML("", &Map{})
 	require.NoError(t, err)
 	require.False(t, found)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shared_state_test.go` around lines 703 - 730, The failing assertions come
from calling SetMsgPack/SetCBOR and GetMsgPack/GetCBOR while the App was created
with New(Config{SharedStorage: newSharedStateMemoryStorage(t)}) without
MsgPack/CBOR codecs; either remove the MsgPack/CBOR Set*/Get* assertions from
the test or provide codecs in the Config passed to New (e.g., set the
MsgPackCodec and CBORCodec fields) so that SetMsgPack, SetCBOR, GetMsgPack and
GetCBOR succeed; locate these calls and update the test to configure codecs on
Config or drop the codec-specific assertions accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@app.go`:
- Around line 1384-1390: OnPostShutdown currently only calls
app.shutdownServices; also close the storage-backed shared state there. In the
app.Hooks().OnPostShutdown callback, after calling
app.shutdownServices(app.servicesShutdownCtx()), check if app.sharedState (or
the field that holds the SharedState) is non-nil and call its Close() method,
logging any error (e.g., log.Errorf("failed to close sharedState: %v", err)).
Ensure you perform the Close() call regardless of service shutdown result and
return nil as before.

In `@shared_state_test.go`:
- Around line 703-730: The failing assertions come from calling
SetMsgPack/SetCBOR and GetMsgPack/GetCBOR while the App was created with
New(Config{SharedStorage: newSharedStateMemoryStorage(t)}) without MsgPack/CBOR
codecs; either remove the MsgPack/CBOR Set*/Get* assertions from the test or
provide codecs in the Config passed to New (e.g., set the MsgPackCodec and
CBORCodec fields) so that SetMsgPack, SetCBOR, GetMsgPack and GetCBOR succeed;
locate these calls and update the test to configure codecs on Config or drop the
codec-specific assertions accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9c645499-2e14-436c-9432-534bbad2540e

📥 Commits

Reviewing files that changed from the base of the PR and between c057a7c and a4946e9.

📒 Files selected for processing (3)
  • app.go
  • shared_state.go
  • shared_state_test.go

@gaby
Copy link
Copy Markdown
Member Author

gaby commented May 1, 2026

@copilot Verify each finding against the current code and only fix it if needed.

In @app.go around lines 1384 - 1390, OnPostShutdown currently only calls
app.shutdownServices; also close the storage-backed shared state there. In the
app.Hooks().OnPostShutdown callback, after calling
app.shutdownServices(app.servicesShutdownCtx()), check if app.sharedState (or
the field that holds the SharedState) is non-nil and call its Close() method,
logging any error (e.g., log.Errorf("failed to close sharedState: %v", err)).
Ensure you perform the Close() call regardless of service shutdown result and
return nil as before.

Copy link
Copy Markdown
Contributor

Copilot AI commented May 1, 2026

@copilot Verify each finding against the current code and only fix it if needed.

In @app.go around lines 1384 - 1390, OnPostShutdown currently only calls
...

Verified the finding against the current code and fixed it in 34c4bc9: the app post-shutdown hook now closes configured shared state after service shutdown, logs any close error, and still attempts the close even when service shutdown reports an error.

Copilot finished work on behalf of gaby May 1, 2026 11:51
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@services_test.go`:
- Around line 260-308: The tests set log.SetOutput to a dummy buffer but restore
it incorrectly; capture the current log output before calling log.SetOutput and
restore that captured writer in t.Cleanup (replace the bytes.NewBuffer(nil)
restore) so later tests aren’t affected, and add t.Parallel() at the start of
each subtest (both "shutdown-hooks/close-shared-state" and
"shutdown-hooks/close-shared-state-after-service-error") to run them in parallel
once the logger sink is isolated; relevant symbols to modify: the two subtest
bodies, the log.SetOutput call, the t.Cleanup restore, and ensure this change is
applied alongside the existing use of shutdownHookStorage, app.initServices, and
app.Hooks().executeOnPostShutdownHooks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 737916fa-4177-4a52-a674-af20c29d73d0

📥 Commits

Reviewing files that changed from the base of the PR and between a4946e9 and 26547f4.

📒 Files selected for processing (2)
  • app.go
  • services_test.go

Comment thread services_test.go
@gaby
Copy link
Copy Markdown
Member Author

gaby commented May 1, 2026

@copilot Verify each finding against the current code and only fix it if needed.

In @services_test.go around lines 260 - 308, The tests set log.SetOutput to a
dummy buffer but restore it incorrectly; capture the current log output before
calling log.SetOutput and restore that captured writer in t.Cleanup (replace the
bytes.NewBuffer(nil) restore) so later tests aren’t affected, and add
t.Parallel() at the start of each subtest (both
"shutdown-hooks/close-shared-state" and
"shutdown-hooks/close-shared-state-after-service-error") to run them in parallel
once the logger sink is isolated; relevant symbols to modify: the two subtest
bodies, the log.SetOutput call, the t.Cleanup restore, and ensure this change is
applied alongside the existing use of shutdownHookStorage, app.initServices, and
app.Hooks().executeOnPostShutdownHooks.

Copy link
Copy Markdown
Contributor

Copilot AI commented May 1, 2026

@copilot Verify each finding against the current code and only fix it if needed.

In @services_test.go around lines 260 - 308, The tests set log.SetOutput to a
...

Fixed in 6543581: the two shutdown-hook subtests now call t.Parallel(), capture and restore the previous logger writer instead of restoring to a dummy buffer, and use a panic-safe helper plus mutex so the temporary global log sink stays isolated.

Copilot finished work on behalf of gaby May 1, 2026 12:10
@gaby
Copy link
Copy Markdown
Member Author

gaby commented May 2, 2026

@claude[agent] Audit all the changes made in this Pull Request to implement the SharedState feature.

@Claude
Copy link
Copy Markdown
Contributor

Claude AI commented May 2, 2026

@claude[agent] Audit all the changes made in this Pull Request to implement the SharedState feature.

SharedState Implementation Audit Report

I've completed a comprehensive audit of all changes in this PR implementing the SharedState feature. All implementation aspects are correct and complete.

✅ Core Implementation (shared_state.go - 416 lines)

Architecture:

  • Properly integrates with fiber.Storage interface for prefork-safe multi-process state sharing
  • Clean separation between process-local State() and storage-backed SharedState()
  • Key namespacing with configurable prefix (defaults to defaultSharedStatePrefix + AppName)

Methods Implemented:

  • Raw: Set/Get/Delete/Has with context variants
  • JSON: SetJSON/GetJSON with context variants (uses app-configured codecs)
  • MsgPack: SetMsgPack/GetMsgPack with context variants
  • CBOR: SetCBOR/GetCBOR with context variants
  • XML: SetXML/GetXML with context variants
  • Lifecycle: Reset/ResetWithContext/Close

Code Quality Improvements (from review feedback):

  • Refactored duplicated encode/decode logic into shared helpers setEncodedWithContext and getEncodedWithContext
  • Consistent empty-key handling across all methods (treated as no-op)
  • Nil codec protection with descriptive errors instead of panics
  • Panic recovery for app-configured placeholder codecs
  • Reduced constructor from 10 parameters to config-based initialization

✅ App Integration (app.go - 31 lines changed)

Configuration:

  • Config.SharedStorage - storage backend (any fiber.Storage implementation)
  • Config.SharedStatePrefix - optional namespace prefix
  • Initialization at line 657: app.sharedState = newSharedState(&app.config)
  • Public accessor at line 1198-1201: App.SharedState()

Shutdown Hook:

  • Properly closes shared state storage in OnPostShutdown hook (lines 1389-1393)
  • Logs errors but doesn't fail shutdown
  • Executes after service shutdown, handles both success and error cases

✅ Test Coverage (shared_state_test.go - 737 lines)

20 test functions covering:

  • Unconfigured storage error handling
  • Prefork-safe multi-worker coordination
  • Context propagation and cancellation
  • Key namespacing collision prevention
  • Codec integration (JSON/MsgPack/CBOR/XML)
  • Custom app codec usage
  • Nil codec error handling (instead of panic)
  • Empty key no-op behavior
  • Storage error propagation
  • Nil receiver safety
  • Data immutability (Get returns copy)
  • Serialization/deserialization edge cases

Shutdown hook tests (services_test.go - 127 lines added):

  • Test helper withCapturedLogOutput with proper mutex protection
  • Verifies shared state closes on shutdown
  • Verifies close happens even after service errors
  • Tests both successful and error close scenarios

✅ Documentation (141 lines added)

docs/api/state.md:

  • Complete SharedState section with method signatures
  • Configuration examples
  • Prefork-safe coordination examples
  • Context-aware timeout/cancellation examples
  • Memory storage caveat clearly documented

docs/api/app.md:

  • Added State/SharedState distinction
  • Links to State Management docs

docs/whats_new.md:

  • Listed SharedState in v3 new methods section

✅ All Quality Checks Pass

  • ✅ All SharedState tests passing (20/20)
  • ✅ Linter: 0 issues
  • ✅ Code properly formatted
  • ✅ No security vulnerabilities
  • ✅ Proper error handling throughout
  • ✅ Consistent API patterns with Fiber conventions

Key Design Decisions

  1. Empty key handling: Consistent no-op behavior (returns nil/false) rather than error, aligning with storage interface expectations
  2. Codec safety: Nil codecs return descriptive errors; placeholder codecs that panic are caught and surfaced as errors
  3. Namespacing: Automatic prefix generation includes AppName when set, reducing collision risk in shared storage
  4. Lifecycle: SharedState storage closes in post-shutdown hook, after services terminate
  5. Immutability: Get() returns a copy to prevent external mutations of cached data

The implementation is production-ready and fully addresses the review feedback from @ReneWerner87 and @copilot-pull-request-reviewer.

Claude finished work on behalf of gaby May 2, 2026 13:52
@ReneWerner87 ReneWerner87 merged commit 3e610ae into main May 3, 2026
21 of 22 checks passed
@ReneWerner87 ReneWerner87 deleted the implement-shared-state-component-using-fiber.storage branch May 3, 2026 12:11
@github-project-automation github-project-automation Bot moved this from In Progress to Done in v3 May 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

5 participants