Skip to content

SQLite KV Database Incompatibility Between eKuiper 1.x and 2.x #3894

@LeeShan87

Description

@LeeShan87

eKuiper 2.x cannot read streams and rules created by eKuiper 1.x due to a breaking change in the SQLite storage format. This causes error unmarshall errors and makes the database effectively corrupted after upgrading.

Environment

  • eKuiper 1.x version: 1.1.1
  • eKuiper 2.x version: 2.1.2
  • Database: sqliteKV.db

Reproduction Steps

  1. Start eKuiper 1.1.1
  2. Create streams and rules via REST API
  3. Stop eKuiper and upgrade to version 2.1.2
  4. Start eKuiper 2.1.2
  5. Attempt to describe or use existing streams

Observed Behavior

time="2025-10-15T13:37:18Z" level=error msg="describe stream error: Describe stream fails, error unmarshall <stream_name>, the data in db may be corrupted."

All existing streams become unusable and cannot be deleted via REST API.

Root Cause Analysis

The SQLite storage format changed between versions:

Database Schema Changes

eKuiper 1.1.1 - 3 tables:

sqlite3 /kuiper/data/sqliteKV.db ".tables"
rule    sink    stream

eKuiper 2.1.2 - 20+ tables:

sqlite3 /kuiper/data/sqliteKV.db ".tables"
asyncManager              portablePluginStatus      services
confKVStorage             rule                      sinkConfigStatus
connectionConfigStatus    ruleStatus                sourceConfigStatus
eKuiperMeta_bump_version  schema                    stream
nativePlugin              schemaStatus              streamStatus
nativePluginStatus        serviceFuncs              tableStatus
pluginFuncs               serviceInstall            uploads
portablePlugin            serviceInstallStatus      uploadsStatusDb

Storage Format Changes

Streams - Changed from plain SQL to JSON wrapper:

# eKuiper 1.1.1
sqlite3 /kuiper/data/sqliteKV.db "SELECT hex(val) FROM stream LIMIT 1;" | xxd -r -p
create stream my_stream () WITH ( DATASOURCE = "topic", FORMAT = "JSON", TYPE="mqtt")

# eKuiper 2.1.2
sqlite3 /kuiper/data/sqliteKV.db "SELECT hex(val) FROM stream LIMIT 1;" | xxd -r -p
{"streamType":0,"streamKind":"","statement":"create stream my_stream () WITH ( DATASOURCE = \"topic\", FORMAT = \"JSON\", TYPE=\"mqtt\")"}

Rules - JSON structure changed (removed triggered field, reordered fields):

# eKuiper 1.1.1
sqlite3 /kuiper/data/sqliteKV.db "SELECT hex(val) FROM rule LIMIT 1;" | xxd -r -p
{"triggered":true,"id":"rule1","sql":"SELECT * FROM stream1","actions":[...],"options":{...}}

# eKuiper 2.1.2
sqlite3 /kuiper/data/sqliteKV.db "SELECT hex(val) FROM rule LIMIT 1;" | xxd -r -p
{"id":"rule1","sql":"SELECT * FROM stream1","actions":[...]}

REST API Limitation

Once the database is corrupted, the REST API cannot be used to fix it:

GET Request (fails)

GET /streams/my_stream

HTTP/1.1 400 Bad Request
{
  "error": 3000,
  "message": "describe stream error: Describe stream fails, error unmarshall my_stream, the data in db may be corrupted."
}

DELETE Request (also fails)

DELETE /streams/my_stream

HTTP/1.1 400 Bad Request
{
  "error": 3000,
  "message": "delete stream error: error unmarshall my_stream, the data in db may be corrupted"
}

Workarounds

Option 1: Clean Installation (Recommended for upgrades)

# Stop eKuiper
docker stop ekuiper

# Remove old database
rm -rf /kuiper/data/sqliteKV.db

# Start eKuiper 2.x (creates fresh database)
docker start ekuiper

# Re-create all streams and rules

Option 2: Use Separate Database File

Modify etc/kuiper.yaml before upgrading:

store:
  sqlite:
    name: sqliteKV-v2.db  # Use different filename for v2.x

This preserves the old database and allows rollback if needed.

Option 3: Direct Database Manipulation (if already corrupted)

# Remove specific corrupted stream
docker exec ekuiper sqlite3 /kuiper/data/sqliteKV.db \
  "DELETE FROM stream WHERE key = 'my_stream';"

# Restart eKuiper
docker restart ekuiper

Notes

  • This issue occurs specifically when upgrading an existing deployment with a populated database
  • Fresh installations of eKuiper 2.x are not affected
  • The database format change appears to have been introduced between version 1.x and 2.x series
  • In our testing environment, this was not caught initially because integration tests always start with a fresh database

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions