mcpField
T
tigris-egress-optimizer

tigris-egress-optimizer

github.com/tigrisdataCloud & DevOps276 installsUpdated 14w ago
Repository

About tigris-egress-optimizer

A Tigris storage skill for diagnosing and reducing excessive object-storage egress costs. It helps developers inspect usage and code patterns, apply caching and delivery changes, then verify bandwidth results.

Key features

  1. 01Inspect bucket bandwidth and object sizes
  2. 02Find common egress-heavy code patterns
  3. 03Use metadata checks instead of full downloads
  4. 04Apply cache headers and direct file delivery
  5. 05Verify bandwidth changes over time

Use cases

  1. 01Investigate unexpectedly high storage transfer costs
  2. 02Reduce repeat downloads of static assets
  3. 03Avoid proxying object downloads through an app server
  4. 04Serve smaller image thumbnails

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/tigrisdata/skills --skill tigris-egress-optimizer

After installing, type/tigris-egress-optimizerin Claude Code to confirm it loaded.

Bundled filesmetadata.jsonREADME.md

Details

From the project README

Tigris Egress Optimizer

Diagnose and fix excessive storage egress (network data transfer) costs. Follow this 4-step framework to identify anti-patterns, apply fixes, and verify savings.

Most high egress bills come from the application fetching more data than it uses — not from infrastructure issues.

Prerequisites

Before doing anything else, install the Tigris CLI if it's not already available:

tigris help || npm install -g @tigrisdata/cli

If you need to install it, tell the user: "I'm installing the Tigris CLI (@tigrisdata/cli) so we can work with Tigris object storage."


Step 1: Diagnose

Identify which buckets and access patterns consume the most bandwidth.

Check Bandwidth Metrics
# Check bucket usage
tigris usage

# List objects by size (find large files being served frequently)
tigris ls t3://my-bucket --recursive -l | sort -k3 -rn | head -20
Questions to Answer
  • Which buckets have the highest bandwidth?
  • Are large files being downloaded repeatedly?
  • Are objects being accessed through the app server or directly?
  • Are there objects being downloaded that are never displayed to users?

Step 2: Analyze Codebase

Check your application code for these common anti-patterns:

Anti-Pattern Checklist
Anti-PatternSymptomImpact
Downloading full objects for metadataUsing get() when head() would sufficeHigh — downloads entire file
Re-downloading immutable assetsNo caching layer for static contentHigh — repeated bandwidth
Proxying through app serverServer downloads then re-serves to client2x bandwidth (Tigris→server + server→client)
Missing Cache-Control headersBrowsers re-fetch on every page loadVery high — multiplied by users
Not using CDNPrivate bucket for public contentMedium — single origin, no edge caching
Downloading full objects for thumbnailsFetching 5MB image to show 100px thumbnailHigh — 50x more data than needed
Code Search

Look for these patterns in your codebase:

# Downloads that should be head() calls
grep -rn "get(" --include="*.ts" --include="*.js" | grep -v "test"

# Missing cache headers on put()
grep -rn "put(" --include="*.ts" --include="*.js" | grep -v "cacheControl\|Cache-Control"

# Server-side file proxying
grep -rn "pipe\|stream\|createReadStream" --include="*.ts" --include="*.js"

Step 3: Fix

Fix 1: Use head() Instead of get() for Metadata
// Before — downloads entire file just to check if it exists
const result = await get("avatars/user-123.jpg", "file");
if (result.error) console.log("not found");

// After — only fetches metadata (size, contentType, modified)
const result = await head("avatars/user-123.jpg");
if (result.error) console.log("not found");
console.log(result.data?.size, result.data?.contentType);
Fix 2: Add Cache-Control Headers
// Set cache headers on upload
await put("assets/logo.png", file, {
  access: "public",
  contentType: "image/png",
});

Recommended cache values:

Content TypeCache-Control
Hashed assets (JS, CSS)public, max-age=31536000, immutable
Images (avatars, uploads)public, max-age=86400 (1 day)
Dynamic contentprivate, no-cache
Fontspublic, max-age=31536000, immutable
Fix 3: Use Public Buckets for CDN

Tigris public buckets automatically serve from the nearest global edge — no separate CDN setup needed.

# Make bucket public
tigris buckets create my-public-assets --public

# Or update existing bucket
tigris buckets update my-bucket --public

Public bucket URLs are served from Tigris's global edge network. This eliminates the need for CloudFront, Cloudflare, or other CDN layers for basic static asset delivery.

Fix 4: Use Presigned URLs (Skip Server Proxy)
// Before — server downloads file, then sends to client (2x egress)
app.get("/download/:path", async (req, res) => {
  const result = await get(req.params.path, "stream");
  result.data.pipe(res);
});

// After — redirect client to download directly from Tigris (1x egress)
app.get("/download/:path", async (req, res) => {
  const result = await getPresignedUrl(req.params.path, {
    operation: "get",
    expiresIn: 300,
  });
  res.redirect(result.data!.url);
});
Fix 5: Store Thumbnails Separately
// Before — serving 5MB original for a 100px avatar
<img src="/api/files/avatars/user-123.jpg" width="100" />

// After — generate and store thumbnail on upload
import sharp from "sharp";

const thumb = await sharp(originalBuffer)
  .resize(100, 100, { fit: "cover" })
  .jpeg({ quality: 80 })
  .toBuffer();

await put("avatars/user-123-thumb.jpg", thumb, {
  access: "public",
  contentType: "image/jpeg",
});
// Serve the 5KB thumbnail instead of the 5MB original
Fix 6: Client-Side Caching with ETags
// Server returns ETag on first request
app.get("/api/config", async (req, res) => {
  const result = await head("config/app.json");
  const etag = result.data?.modified?.toISOString();

  if (req.headers["if-none-match"] === etag) {
    return res.status(304).end(); // No body sent, no egress
  }

  const file = await get("config/app.json", "string");
  res.set("ETag", etag);
  res.json(JSON.parse(file.data));
});
Fix 7: Regional Pinning

Keep data close to compute to reduce cross-region transfer:

# Pin bucket to specific regions
tigris buckets create my-bucket --locations us-east-1,eu-west-1

Step 4: Verify

After applying fixes:

  1. Monitor bandwidth — check Tigris dashboard for bandwidth reduction
  2. Compare before/after — track bandwidth for 1-2 weeks
  3. Set alerts — configure monitoring for unexpected bandwidth spikes
  4. Review periodically — new features can introduce new anti-patterns

Quick Wins Summary

FixEffortImpact
Add Cache-Control headersLowHigh
Switch to public bucket (CDN)LowHigh
Use presigned URLs instead of proxyingMediumHigh
Replace get() with head() for checksLowMedium
Store thumbnails separatelyMediumHigh
Regional pinningLowMedium

Critical Rules

Always: Use public buckets for content served to users | Set Cache-Control headers on every upload | Use presigned URLs for private file downloads | Use head() for existence/metadata checks

Never: Proxy files through your app server when presigned URLs work | Serve original images when thumbnails suffice | Skip caching for immutable assets | Ignore bandwidth metrics


Related Skills

  • tigris-image-optimization — Generate thumbnails and variants
  • tigris-static-assets — CDN delivery with cache headers
  • tigris-lifecycle-management — Move cold data to cheaper tiers

Official Documentation

Comments

Markdown supported