fix(features): increment version to deal with removed event types (#11402)

# Which Problems Are Solved

User with older instances, created at version 3.3.3 or earlier may have
been affected with feature flags no longer applying. Older version of
Zitadel had a bug in console where toggling a single feature would apply
a state to all features, creating around 12 events each request. Then 4
feature flags and their event types were removed in v4, meaning that the
`offset` value in the projection's current state could be wrong by 4 if
the instance was affected by the above bug.

# How the Problems Are Solved

Increment the feature projection versions, so that the tables get
recomputed and the current states entries becomes accurate again.

# Additional Changes

- none

# Additional Context

- Original bug: https://github.com/zitadel/zitadel/issues/10459
- Reported through support
This commit is contained in:
Tim Möhlmann
2026-01-22 09:21:56 +00:00
committed by GitHub
parent 7d6a2950b4
commit 4ac16a119f
6 changed files with 12 additions and 12 deletions
+2 -2
View File
@@ -6,14 +6,14 @@ with domain as (
), instance_features as (
select i.*
from domain d
join projections.instance_features2 i on d.instance_id = i.instance_id
join projections.instance_features3 i on d.instance_id = i.instance_id
), features as (
select instance_id, json_object_agg(
coalesce(i.key, s.key),
coalesce(i.value, s.value)
) features
from domain d
cross join projections.system_features s
cross join projections.system_features2 s
full outer join instance_features i using (instance_id, key)
group by instance_id
), external_domains as (
+2 -2
View File
@@ -4,8 +4,8 @@ with features as (
coalesce(i.value, s.value)
) features
from (select $1::text instance_id) x
cross join projections.system_features s
full outer join projections.instance_features2 i using (key, instance_id)
cross join projections.system_features2 s
full outer join projections.instance_features3 i using (key, instance_id)
group by instance_id
), external_domains as (
select instance_id, array_agg(domain) as domains
@@ -14,7 +14,7 @@ import (
)
const (
InstanceFeatureTable = "projections.instance_features2"
InstanceFeatureTable = "projections.instance_features3"
InstanceFeatureInstanceIDCol = "instance_id"
InstanceFeatureKeyCol = "key"
@@ -38,7 +38,7 @@ func TestInstanceFeaturesProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO projections.instance_features2 (instance_id, key, creation_date, change_date, sequence, value) VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (instance_id, key) DO UPDATE SET (creation_date, change_date, sequence, value) = (projections.instance_features2.creation_date, EXCLUDED.change_date, EXCLUDED.sequence, EXCLUDED.value)",
expectedStmt: "INSERT INTO projections.instance_features3 (instance_id, key, creation_date, change_date, sequence, value) VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (instance_id, key) DO UPDATE SET (creation_date, change_date, sequence, value) = (projections.instance_features3.creation_date, EXCLUDED.change_date, EXCLUDED.sequence, EXCLUDED.value)",
expectedArgs: []interface{}{
"agg-id",
"user_schema",
@@ -69,7 +69,7 @@ func TestInstanceFeaturesProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO projections.instance_features2 (instance_id, key, creation_date, change_date, sequence, value) VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (instance_id, key) DO UPDATE SET (creation_date, change_date, sequence, value) = (projections.instance_features2.creation_date, EXCLUDED.change_date, EXCLUDED.sequence, EXCLUDED.value)",
expectedStmt: "INSERT INTO projections.instance_features3 (instance_id, key, creation_date, change_date, sequence, value) VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (instance_id, key) DO UPDATE SET (creation_date, change_date, sequence, value) = (projections.instance_features3.creation_date, EXCLUDED.change_date, EXCLUDED.sequence, EXCLUDED.value)",
expectedArgs: []interface{}{
"instance-id",
"login_default_org",
@@ -100,7 +100,7 @@ func TestInstanceFeaturesProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM projections.instance_features2 WHERE (instance_id = $1)",
expectedStmt: "DELETE FROM projections.instance_features3 WHERE (instance_id = $1)",
expectedArgs: []interface{}{
"agg-id",
},
@@ -126,7 +126,7 @@ func TestInstanceFeaturesProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM projections.instance_features2 WHERE (instance_id = $1)",
expectedStmt: "DELETE FROM projections.instance_features3 WHERE (instance_id = $1)",
expectedArgs: []interface{}{
"agg-id",
},
+1 -1
View File
@@ -12,7 +12,7 @@ import (
)
const (
SystemFeatureTable = "projections.system_features"
SystemFeatureTable = "projections.system_features2"
SystemFeatureKeyCol = "key"
SystemFeatureCreationDateCol = "creation_date"
@@ -36,7 +36,7 @@ func TestSystemFeaturesProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO projections.system_features (key, creation_date, change_date, sequence, value) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (key) DO UPDATE SET (creation_date, change_date, sequence, value) = (projections.system_features.creation_date, EXCLUDED.change_date, EXCLUDED.sequence, EXCLUDED.value)",
expectedStmt: "INSERT INTO projections.system_features2 (key, creation_date, change_date, sequence, value) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (key) DO UPDATE SET (creation_date, change_date, sequence, value) = (projections.system_features2.creation_date, EXCLUDED.change_date, EXCLUDED.sequence, EXCLUDED.value)",
expectedArgs: []interface{}{
"user_schema",
anyArg{},
@@ -66,7 +66,7 @@ func TestSystemFeaturesProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM projections.system_features WHERE (key IS NOT NULL)",
expectedStmt: "DELETE FROM projections.system_features2 WHERE (key IS NOT NULL)",
expectedArgs: []interface{}{},
},
},