Skip to main content

Performance Mode — Speed Up Your API

· 9 min read

Update checks are bursty and repetitive. Every running copy of your app polls the same /checkVersion endpoint with nearly identical parameters, and each call validates the app, channel, platform, and architecture against MongoDB. At a few hundred clients that's fine. At thousands it's the same handful of database queries, repeated thousands of times a minute.

Performance Mode fixes that with caching. This post explains how it works, how to configure Redis, how cache invalidation keeps results correct, and how to stack an optional Nginx microcache on top for a multi-layer strategy.


What is Performance Mode? 🤔​

Performance Mode is a Redis-backed caching layer for the API that:

  • 🚀 Reduces response times
  • 📉 Lowers database load
  • 💾 Optimizes resource usage

When enabled, faynoSync caches the answers of its two hottest read endpoints in Redis and serves repeat requests straight from memory instead of re-querying MongoDB. See the Performance Mode docs for the reference.


How It Works 🛠️​

                 ┌─────────────────────────────┐
client ──────▶ │ GET /checkVersion │
└──────────────┬──────────────┘
│
PERFORMANCE_MODE=true?
│
┌──────────────┴──────────────┐
▼ ▼
look up in Redis (disabled) query
│ MongoDB every time
┌────────┴────────┐
▼ ▼
cache HIT cache MISS
return from query MongoDB,
Redis (fast) store in Redis,
then return
  1. Request arrives — the API checks whether PERFORMANCE_MODE=true.
  2. Cache check — it looks for cached data in Redis for that request.
    • HIT → return immediately.
    • MISS → query MongoDB, store the result in Redis, then return.
  3. Next request — the same query is now a cache hit.

What gets cached?​

EndpointWho calls itCached
/checkVersionEvery running copy of your app, on every update checkYes
/apps/latestDownload buttons, install scripts, CIYes
Anything for a private appClients with a download tokenNever

Private apps are skipped on purpose: their answers depend on who is asking, so a shared cache entry could leak them to someone without access.

Every cached entry lives for 24 hours at most, and is usually cleared much earlier by the invalidation rules below.

Updated in September 2026

This post originally said Performance Mode covers only /checkVersion. It now caches /apps/latest as well, and cache invalidation covers more than new uploads. The sections below reflect the current behavior.


Why Do We Need It? 🎯​

  • Database load reduction — far fewer MongoDB queries for the same repeated update checks.
  • Faster response times — cached responses come from memory, no database round-trip.
  • Resource optimization — lower CPU and DB pressure means the same hardware scales further.

This matters most exactly when it's hardest: traffic spikes right after you publish a release and every client checks in at once.


How to Enable It 🔌​

Enable the mode and point faynoSync at your Redis instance in .env:

PERFORMANCE_MODE=true
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
VariableDefaultDescription
PERFORMANCE_MODEfalseMaster switch for Redis caching
REDIS_HOSTlocalhostRedis hostname or IP
REDIS_PORT6379Redis port
REDIS_PASSWORD(empty)Leave empty if Redis has no auth
REDIS_DB0Redis database number

That's it — once PERFORMANCE_MODE=true and Redis is reachable, the API starts caching automatically.

Note that Redis isn't an extra dependency you add for this feature: faynoSync already requires Redis and won't start without it. PERFORMANCE_MODE only decides whether responses get cached there. /health pings Redis whether or not the mode is on, and reports unhealthy when it's unreachable — handy for load balancer health checks. Full reference in the environment overview.


Cache invalidation — staying correct 🔄​

A cache is only useful if it never serves stale "latest version" info. faynoSync handles this for you:

The cache of an app's channel is cleared whenever something in that channel changes what clients should see:

  • a version is uploaded with a publish value (through /upload or presigned uploads),
  • a version is updated with a publish value — publishing, unpublishing, or editing it in the dashboard,
  • a version is deleted.

The next request after that goes to MongoDB, gets the fresh answer, and caches it again. So the moment you publish a release — or pull a broken one — the old cached answer is gone. No manual flushing, no risk of pinning users to an old build.

Invalidation is scoped to the channel. Publishing to beta doesn't throw away the warm cache for stable.


Going further: add an Nginx microcache 🧱​

For very high traffic you can stack a second cache layer in front of the API. Nginx can microcache /checkVersion responses for a few seconds, absorbing bursts before they ever reach faynoSync.

# In the http block
proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=checkversion_cache:10m inactive=60s max_size=100m;

# In the server block
location = /checkVersion {
proxy_pass http://127.0.0.1:9000;

proxy_cache checkversion_cache;
proxy_cache_valid 200 60s;
proxy_cache_key "$scheme$request_method$host$request_uri";

# Serve stale cache if the backend is slow or erroring
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_lock on;
proxy_cache_background_update on;

add_header X-Cache-Status $upstream_cache_status;
}

This gives you a two-layer strategy: Nginx absorbs identical requests for ~60s at the edge of your server, and Redis (Performance Mode) handles everything that gets through. The X-Cache-Status header tells you what happened:

  • HIT — served from Nginx
  • MISS — went to the backend
  • UPDATING — cache being refreshed in the background
  • STALE — backend was unhealthy, stale copy served

The full Nginx block (with rate limiting and proxy headers) is in the Performance Mode docs.

Two things to keep in mind with Nginx caching​

1. Nginx doesn't know when you publish. faynoSync clears its Redis cache the moment a release changes, but it can't reach into Nginx. With proxy_cache_valid 200 60s, clients can keep getting the previous answer for up to 60 seconds after you publish — or after you unpublish a broken version. For most apps a minute is fine. If you need rollbacks to take effect faster, lower the value to a few seconds: even a short microcache still absorbs the burst.

2. Don't share cached answers of private apps. Clients of a private app in strict mode send an X-Download-Token header, and the cache key above doesn't include it. Without extra rules, Nginx could store a private answer and hand it to a request without a token. Skip the Nginx cache for any request that carries credentials:

location = /checkVersion {
# ... everything from above ...

proxy_cache_bypass $http_x_download_token $http_authorization;
proxy_no_cache $http_x_download_token $http_authorization;
}

faynoSync itself never caches private apps in Redis, so this only matters for the Nginx layer.


Measuring the difference 🧪​

Don't guess — measure on your own hardware. This script fires 1000 sequential /checkVersion requests and reports timing:

start_time=$(date +%s);
for i in {1..1000}; do
response=$(curl -s -w "Connect Time: %{time_connect}s, Start Transfer Time: %{time_starttransfer}s, Total Time: %{time_total}s\n" -o /dev/stdout --location 'http://localhost:9000/checkVersion?app_name=myapp&version=0.0.3&channel=nightly&platform=null&arch=null');
echo "$response";
done;
end_time=$(date +%s);
total_time=$((end_time - start_time));
echo "Total execution time: $total_time seconds"

Run it twice — once with PERFORMANCE_MODE=false, once with true — and compare Total execution time and Start Transfer Time (time to first byte). Absolute numbers depend on your hardware, network, and Redis setup, so treat your own measurements as the source of truth.


When to use it ⚡​

Use Performance Mode when:

  • You expect high or bursty traffic (especially right after publishing releases).
  • Many clients poll /checkVersion frequently.
  • Response time is critical.

It matters less when:

  • You have a small, low-traffic deployment.
  • Update checks are rare.

In production with real users, the recommendation is simple: turn it on.


Performance Mode vs Nginx vs Edge caching​

faynoSync gives you three independent cache layers. You can use one, two, or all three:

Performance ModeNginx microcacheEdge + S3 cache
WhereRedis, inside the APIYour reverse proxyObject storage / CDN
What it savesMongoDB queriesRequests reaching the APIRequests reaching your server at all
Freshness after publishImmediate (invalidated)Up to proxy_cache_validInvalidated by faynoSync
ClientsAnyAnyOfficial SDKs with EdgeURL
SetupOne env variableNginx configBucket + SDK config

A good path for most teams: turn on Performance Mode first, add the Nginx microcache when a single API instance gets busy, and move to edge caching when you have a large or global install base. The why and how of that last step is in Scaling Update Checks with Edge + S3 Response Cache.


How to try faynoSync?​

  1. Follow the Getting Started guide:
    👉 https://faynosync.com/docs/getting-started

  2. Create your app using the REST API or web dashboard:
    📦 API Docs: https://faynosync.com/docs/api
    🖥️ Dashboard UI: https://github.com/ku9nov/faynoSync-dashboard

  3. Upload at least two versions of your application.

  4. Check for updates with this simple request:
    📡 /info/latest



If you find this project helpful, please consider subscribing, leaving a comment, or giving it a star, create an Issue or feature request on GitHub.
Your support keeps the project alive and growing 💚