From c1f7cfccdac16dd96d96f376b272ada6c5f2809e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emirhan=20Durmu=C5=9F?= Date: Mon, 13 Apr 2026 17:58:40 +0300 Subject: [PATCH] rbac cache increment version logic fixed --- .../managers/rbac-cache-version-manager.js | 90 +++++++++++++++++-- 1 file changed, 82 insertions(+), 8 deletions(-) diff --git a/src/data/managers/rbac-cache-version-manager.js b/src/data/managers/rbac-cache-version-manager.js index 73666a65..2b69a17a 100644 --- a/src/data/managers/rbac-cache-version-manager.js +++ b/src/data/managers/rbac-cache-version-manager.js @@ -14,6 +14,7 @@ const BaseManager = require('./base-manager') const models = require('../models') const RbacCacheVersion = models.RbacCacheVersion +const logger = require('../../logger') class RbacCacheVersionManager extends BaseManager { getEntity () { @@ -33,6 +34,64 @@ class RbacCacheVersionManager extends BaseManager { return cacheVersion.version || 0 } + _getModelOptions (transaction) { + return transaction && transaction.fakeTransaction + ? {} + : { transaction: transaction } + } + + _extractAffectedRows (updateResult) { + if (Array.isArray(updateResult)) { + return Number(updateResult[0] || 0) + } + return Number(updateResult || 0) + } + + _isUniqueConstraintError (error) { + return error && error.name === 'SequelizeUniqueConstraintError' + } + + _isVersionOverflowError (error) { + if (!error || error.name !== 'SequelizeDatabaseError') { + return false + } + const message = (error.message || '').toLowerCase() + return message.includes('out of range for type bigint') || + message.includes('bigint value is out of range') || + message.includes('integer overflow') || + message.includes('numeric value out of range') + } + + async _incrementVersionAtomic (transaction) { + return this.getEntity().update( + { version: models.Sequelize.literal('version + 1') }, + { + where: { id: 1 }, + ...this._getModelOptions(transaction) + } + ) + } + + async _resetVersionToOne (transaction) { + const updateResult = await this.getEntity().update( + { version: 1 }, + { + where: { id: 1 }, + ...this._getModelOptions(transaction) + } + ) + + if (this._extractAffectedRows(updateResult) === 0) { + try { + await this.create({ id: 1, version: 1 }, transaction) + } catch (error) { + if (!this._isUniqueConstraintError(error)) { + throw error + } + } + } + } + /** * Increment cache version * This should be called whenever any RBAC resource (Role, RoleBinding, ServiceAccount) is modified @@ -40,15 +99,30 @@ class RbacCacheVersionManager extends BaseManager { * @returns {Promise} */ async incrementVersion (transaction) { - const cacheVersion = await this.findOne({ id: 1 }, transaction) + try { + const updateResult = await this._incrementVersionAtomic(transaction) + const affectedRows = this._extractAffectedRows(updateResult) - if (cacheVersion) { - // Update existing version - const newVersion = (cacheVersion.version || 0) + 1 - await this.update({ id: 1 }, { version: newVersion }, transaction) - } else { - // Create initial version if it doesn't exist - await this.create({ id: 1, version: 1 }, transaction) + if (affectedRows === 0) { + try { + await this.create({ id: 1, version: 1 }, transaction) + } catch (error) { + if (this._isUniqueConstraintError(error)) { + await this._incrementVersionAtomic(transaction) + } else { + throw error + } + } + } + } catch (error) { + if (!this._isVersionOverflowError(error)) { + throw error + } + + logger.warn(`RBAC cache version overflow detected. Resetting version to 1. Error: ${error.message}`) + await this._resetVersionToOne(transaction) + await this._incrementVersionAtomic(transaction) + logger.info('RBAC cache version reset and increment completed successfully') } }