Skip to content

fix: auto-detect serverless runtime in memory-driver, skip file persistence - #827

Merged
hotlong merged 3 commits into
mainfrom
copilot/fix-memory-driver-persistence
Feb 27, 2026
Merged

fix: auto-detect serverless runtime in memory-driver, skip file persistence#827
hotlong merged 3 commits into
mainfrom
copilot/fix-memory-driver-persistence

Conversation

CopilotAI commented Feb 27, 2026

Copy link
Copy Markdown
Contributor

persistence: 'auto' (the default) silently selects the file-system adapter in serverless environments where the filesystem is ephemeral or read-only, causing complete data loss with no warning.

Changes

  • Runtime detectionisServerlessEnvironment() checks well-known env vars (VERCEL, AWS_LAMBDA_FUNCTION_NAME, NETLIFY, FUNCTIONS_WORKER_RUNTIME, K_SERVICE, FUNCTION_TARGET, DENO_DEPLOYMENT_ID)
  • Auto mode behavior — browser → localStorage, serverless → disabled + logger.warn(), Node.js → file. Explicit persistence: 'file' or custom adapter are unaffected.
  • Schema docs — JSDoc on AutoPersistenceConfigSchema, MemoryConfigSchema.persistence, and PersistenceTypeSchema updated to document serverless behavior
  • Tests — 4 new cases: auto object config under VERCEL, auto shorthand under AWS_LAMBDA, explicit file still works in serverless, custom adapter still works in serverless
// Before: silently creates FileSystemPersistenceAdapter on Vercel → data lostnewInMemoryDriver()// persistence defaults to 'auto'// After: logs warning, runs as pure in-memory// [WARN] Serverless environment detected — file-system persistence is disabled in auto mode.// Set persistence: false to silence this warning, or provide a custom adapter// (e.g. Upstash Redis, Vercel KV) via persistence: { adapter: yourAdapter }.// Recommended serverless configs:newInMemoryDriver({persistence: false})newInMemoryDriver({persistence: {adapter: upstashAdapter}})
Original prompt

This section details on the original issue you should resolve

<issue_title>【严重缺陷】memory-driver 在 serverless (Vercel) 环境下默认数据持久化策略导致数据静默丢失</issue_title>
<issue_description>## Background
The current memory-driver persistence mechanism causes critical hidden failures on Vercel and other serverless function platforms:

  • Default persistence chooses file-system adapter, which is not safe or persistent in a serverless environment.
  • Read-only paths or short-lived instances lead to complete data loss.
  • Data is not shared across concurrent function instances.
  • Dirty-flag delayed write approach cannot guarantee consistency.

Impact

When deployed on serverless platforms like Vercel, all written data will be lost with no warning or error. All writable use cases are affected.

Fix recommendations

  1. Auto-detect runtime (serverless/edge/node/browser) in the driver. If serverless detected, disable file persistence and display prominent warning.
  2. Update documentation and schema docs to clearly warn about incompatibility in serverless context; instruct users to use { persistence: false } or supply custom adapter.
  3. Add out-of-box config option for serverless-safe setup, providing hooks/samples for Upstash/KV adapters (hooks only, no implementation required).
  4. Ensure auto mode never triggers file-system adapter in serverless.

Suggested schedule

  • P0: Complete runtime detection and warning + docs update.
  • P1: Custom adapter config guidance.

PS: driver-turso should be a separate tracked task, not included in this patch.


Relevant code paths:

  • packages/plugins/driver-memory/src/memory-driver.ts
  • packages/plugins/driver-memory/src/persistence/file-adapter.ts
  • packages/spec/src/data/driver/memory.zod.ts
    </issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@vercel

vercelBot commented Feb 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
objectstack-playReadyReadyPreview, CommentFeb 27, 2026 11:52am
specReadyReadyPreview, CommentFeb 27, 2026 11:52am

Request Review

CopilotAIand others added 2 commits February 27, 2026 11:37
…th warning
In serverless environments (Vercel, AWS Lambda, Netlify, Azure Functions,
Google Cloud Functions, Deno Deploy), auto persistence mode now disables
file-system persistence and emits a warning instead of silently choosing
an adapter that will lose data.
- Add isServerlessEnvironment() detection via well-known env vars
- Auto mode: browser → localStorage, serverless → disabled+warn, Node.js → file
- Explicit 'file' or custom adapter still works in serverless (user's choice)
- Update schema JSDoc to document serverless behavior
- Add 4 new persistence tests for serverless scenarios
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Address code review feedback:
- Extract duplicated warning message to static SERVERLESS_PERSISTENCE_WARNING constant
- Add JSDoc note about process/env unavailability in non-Node.js runtimes
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
CopilotAI changed the title [WIP] Fix memory-driver data loss in serverless environmentsfix: auto-detect serverless runtime in memory-driver, skip file persistenceFeb 27, 2026
@hotlong
hotlong marked this pull request as ready for review February 27, 2026 17:51
CopilotAI review requested due to automatic review settings February 27, 2026 17:51
@hotlong
hotlong merged commit 932f4ce into mainFeb 27, 2026
4 checks passed

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This pull request fixes a critical data loss bug in the memory-driver where the default persistence: 'auto' configuration would silently use file-system persistence in serverless environments (Vercel, AWS Lambda, etc.), leading to complete data loss due to ephemeral/read-only filesystems.

Changes:

  • Adds serverless runtime detection by checking well-known environment variables from major platforms
  • Updates auto-persistence logic to disable file persistence with a warning in serverless environments while browser and explicit configurations remain unaffected
  • Enhances documentation across schema and implementation to clearly warn about serverless behavior and provide actionable alternatives

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

FileDescription
packages/spec/src/data/driver/memory.zod.tsUpdated JSDoc for PersistenceTypeSchema, AutoPersistenceConfigSchema, and MemoryConfigSchema.persistence to document serverless behavior and provide clear guidance on alternatives
packages/plugins/driver-memory/src/memory-driver.tsAdded isServerlessEnvironment() detection method and updated initPersistence() to check serverless environment and emit warning instead of using file adapter in auto mode
packages/plugins/driver-memory/src/persistence/persistence.test.tsAdded comprehensive test suite for serverless environment detection covering auto mode (object and shorthand), explicit file mode, and custom adapters in serverless context

Comment on lines +237 to +260
const filePath = path.join(TEST_DATA_DIR, 'serverless-test.json');
const driver = new InMemoryDriver({
persistence: { type: 'auto', path: filePath },
});
await driver.connect();
await driver.create('items', { id: '1', name: 'Widget' });
await driver.flush();
await driver.disconnect();

// File should NOT have been created because auto mode skips file persistence in serverless
expect(fs.existsSync(filePath)).toBe(false);
});

it('should disable file persistence in auto shorthand mode when AWS_LAMBDA_FUNCTION_NAME is set', async () => {
process.env.AWS_LAMBDA_FUNCTION_NAME = 'my-function';
const driver = new InMemoryDriver({ persistence: 'auto' });
await driver.connect();
await driver.create('items', { id: '1', name: 'Widget' });

// Should work as pure in-memory without errors
const items = await driver.find('items', { object: 'items' });
expect(items).toHaveLength(1);

await driver.disconnect();

CopilotAIFeb 27, 2026

Copy link

Choose a reason for hiding this comment

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

The serverless environment tests verify that file persistence is disabled but don't verify that the warning message is actually logged. Consider adding assertions to verify the warning is emitted, for example by using a mock logger or by spying on logger.warn. This would ensure the warning behavior is tested and prevent regressions where the warning might be accidentally removed.

Suggested change
constfilePath=path.join(TEST_DATA_DIR,'serverless-test.json');
constdriver=newInMemoryDriver({
persistence: {type: 'auto',path: filePath},
});
awaitdriver.connect();
awaitdriver.create('items',{id: '1',name: 'Widget'});
awaitdriver.flush();
awaitdriver.disconnect();
// File should NOT have been created because auto mode skips file persistence in serverless
expect(fs.existsSync(filePath)).toBe(false);
});
it('should disable file persistence in auto shorthand mode when AWS_LAMBDA_FUNCTION_NAME is set',async()=>{
process.env.AWS_LAMBDA_FUNCTION_NAME='my-function';
constdriver=newInMemoryDriver({persistence: 'auto'});
awaitdriver.connect();
awaitdriver.create('items',{id: '1',name: 'Widget'});
// Should work as pure in-memory without errors
constitems=awaitdriver.find('items',{object: 'items'});
expect(items).toHaveLength(1);
awaitdriver.disconnect();
constwarnSpy=vi.spyOn(console,'warn').mockImplementation(()=>{});
try{
constfilePath=path.join(TEST_DATA_DIR,'serverless-test.json');
constdriver=newInMemoryDriver({
persistence: {type: 'auto',path: filePath},
});
awaitdriver.connect();
awaitdriver.create('items',{id: '1',name: 'Widget'});
awaitdriver.flush();
awaitdriver.disconnect();
// File should NOT have been created because auto mode skips file persistence in serverless
expect(fs.existsSync(filePath)).toBe(false);
// A warning should be logged when auto persistence is disabled in a serverless environment
expect(warnSpy).toHaveBeenCalled();
}finally{
warnSpy.mockRestore();
}
});
it('should disable file persistence in auto shorthand mode when AWS_LAMBDA_FUNCTION_NAME is set',async()=>{
process.env.AWS_LAMBDA_FUNCTION_NAME='my-function';
constwarnSpy=vi.spyOn(console,'warn').mockImplementation(()=>{});
try{
constdriver=newInMemoryDriver({persistence: 'auto'});
awaitdriver.connect();
awaitdriver.create('items',{id: '1',name: 'Widget'});
// Should work as pure in-memory without errors
constitems=awaitdriver.find('items',{object: 'items'});
expect(items).toHaveLength(1);
awaitdriver.disconnect();
// A warning should be logged when auto persistence is disabled in a serverless environment
expect(warnSpy).toHaveBeenCalled();
}finally{
warnSpy.mockRestore();
}

Copilot uses AI. Check for mistakes.
Comment on lines +216 to +297
describe('Serverless Environment Detection', () => {
const serverlessEnvVars = [
'VERCEL',
'VERCEL_ENV',
'AWS_LAMBDA_FUNCTION_NAME',
'NETLIFY',
'FUNCTIONS_WORKER_RUNTIME',
'K_SERVICE',
'FUNCTION_TARGET',
'DENO_DEPLOYMENT_ID',
];

afterEach(() => {
// Clean up all serverless env vars after each test
for (const key of serverlessEnvVars) {
delete process.env[key];
}
});

it('should disable file persistence in auto mode when VERCEL env is set', async () => {
process.env.VERCEL = '1';
const filePath = path.join(TEST_DATA_DIR, 'serverless-test.json');
const driver = new InMemoryDriver({
persistence: { type: 'auto', path: filePath },
});
await driver.connect();
await driver.create('items', { id: '1', name: 'Widget' });
await driver.flush();
await driver.disconnect();

// File should NOT have been created because auto mode skips file persistence in serverless
expect(fs.existsSync(filePath)).toBe(false);
});

it('should disable file persistence in auto shorthand mode when AWS_LAMBDA_FUNCTION_NAME is set', async () => {
process.env.AWS_LAMBDA_FUNCTION_NAME = 'my-function';
const driver = new InMemoryDriver({ persistence: 'auto' });
await driver.connect();
await driver.create('items', { id: '1', name: 'Widget' });

// Should work as pure in-memory without errors
const items = await driver.find('items', { object: 'items' });
expect(items).toHaveLength(1);

await driver.disconnect();
});

it('should still allow explicit file persistence in serverless if user requests it', async () => {
process.env.VERCEL = '1';
const filePath = path.join(TEST_DATA_DIR, 'explicit-file-serverless.json');
const driver = new InMemoryDriver({
persistence: { type: 'file', path: filePath, autoSaveInterval: 100 },
});
await driver.connect();
await driver.create('items', { id: '1', name: 'Widget' });
await driver.flush();
await driver.disconnect();

// Explicit 'file' type should still create the file even in serverless
expect(fs.existsSync(filePath)).toBe(true);
});

it('should still allow custom adapter in serverless', async () => {
process.env.NETLIFY = 'true';
const stored: Record<string, any[]> = {};
const customAdapter = {
load: async () => Object.keys(stored).length > 0 ? { ...stored } : null,
save: async (db: Record<string, any[]>) => {
for (const [k, v] of Object.entries(db)) { stored[k] = [...v]; }
},
flush: async () => {},
};

const driver = new InMemoryDriver({ persistence: { adapter: customAdapter } });
await driver.connect();
await driver.create('items', { id: '1', name: 'Widget' });
await driver.disconnect();

expect(stored.items).toBeDefined();
expect(stored.items).toHaveLength(1);
});
});

CopilotAIFeb 27, 2026

Copy link

Choose a reason for hiding this comment

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

Missing test case for edge runtime environments where both localStorage and serverless environment variables might be present (e.g., Vercel Edge Functions with polyfills). Consider adding a test that sets both a serverless env var and mocks localStorage to verify that browser detection takes precedence and uses localStorage persistence. This would document and protect the expected priority order.

Copilot uses AI. Check for mistakes.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

【严重缺陷】memory-driver 在 serverless (Vercel) 环境下默认数据持久化策略导致数据静默丢失

3 participants

@hotlong