From df89fc62326db63c9b1ea2a172cdb2cd6ec2f66b Mon Sep 17 00:00:00 2001 From: Andrey Kravchenko Date: Wed, 14 Jan 2026 09:45:15 +0300 Subject: [PATCH 1/4] Add flush sequences to replication API --- examples/replication/fbSampleReplicator.cpp | 7 ++++ src/include/firebird/FirebirdInterface.idl | 1 + src/include/firebird/IdlFbInterfaces.h | 30 ++++++++++++++ src/include/gen/Firebird.pas | 25 ++++++++++++ src/jrd/replication/Publisher.cpp | 16 ++++++++ src/jrd/replication/Replicator.cpp | 44 ++++++++++++++------- src/jrd/replication/Replicator.h | 33 ++++++++++++---- 7 files changed, 134 insertions(+), 22 deletions(-) diff --git a/examples/replication/fbSampleReplicator.cpp b/examples/replication/fbSampleReplicator.cpp index 8eac456b053..fa62653c48e 100644 --- a/examples/replication/fbSampleReplicator.cpp +++ b/examples/replication/fbSampleReplicator.cpp @@ -37,6 +37,7 @@ class ReplPlugin : public IReplicatedSessionImpl FB_BOOLEAN cleanupTransaction(ISC_INT64 number) override; FB_BOOLEAN deprecatedSetSequence(const char* name, ISC_INT64 value) override; FB_BOOLEAN setSequence2(const char* schemaName, const char* genName, ISC_INT64 value) override; + FB_BOOLEAN flushSequences() override; private: friend class ReplTransaction; @@ -264,6 +265,12 @@ FB_BOOLEAN ReplPlugin::setSequence2(const char* schemaName, const char* genName, return FB_TRUE; } +FB_BOOLEAN ReplPlugin::flushSequences() +{ + WriteLog(log, "%p\tflushSequences()\n"); + return FB_TRUE; +} + ReplTransaction::ReplTransaction(ReplPlugin* session, ITransaction* transaction, ISC_INT64 number): parent(session), trans(transaction) { diff --git a/src/include/firebird/FirebirdInterface.idl b/src/include/firebird/FirebirdInterface.idl index 4bce83e70a4..ff4a644ed8a 100644 --- a/src/include/firebird/FirebirdInterface.idl +++ b/src/include/firebird/FirebirdInterface.idl @@ -1830,6 +1830,7 @@ interface ReplicatedSession : PluginBase version: // 5.0 => 6.0 Alpha1 void setSequence2(Status status, const string schemaName, const string genName, int64 value); + void flushSequences(Status status); } // Profiler interfaces diff --git a/src/include/firebird/IdlFbInterfaces.h b/src/include/firebird/IdlFbInterfaces.h index ea71d25e6bc..dab643ff108 100644 --- a/src/include/firebird/IdlFbInterfaces.h +++ b/src/include/firebird/IdlFbInterfaces.h @@ -7328,6 +7328,7 @@ namespace Firebird void (CLOOP_CARG *cleanupTransaction)(IReplicatedSession* self, IStatus* status, ISC_INT64 number) CLOOP_NOEXCEPT; void (CLOOP_CARG *deprecatedSetSequence)(IReplicatedSession* self, IStatus* status, const char* name, ISC_INT64 value) CLOOP_NOEXCEPT; void (CLOOP_CARG *setSequence2)(IReplicatedSession* self, IStatus* status, const char* schemaName, const char* genName, ISC_INT64 value) CLOOP_NOEXCEPT; + void (CLOOP_CARG *flushSequences)(IReplicatedSession* self, IStatus* status) CLOOP_NOEXCEPT; }; protected: @@ -7385,6 +7386,19 @@ namespace Firebird static_cast(this->cloopVTable)->setSequence2(this, status, schemaName, genName, value); StatusType::checkException(status); } + + template void flushSequences(StatusType* status) + { + if (cloopVTable->version < 5) + { + StatusType::setVersionError(status, "IReplicatedSession", cloopVTable->version, 5); + StatusType::checkException(status); + return; + } + StatusType::clearException(status); + static_cast(this->cloopVTable)->flushSequences(this, status); + StatusType::checkException(status); + } }; #define FIREBIRD_IPROFILER_PLUGIN_VERSION 4u @@ -21387,6 +21401,7 @@ namespace Firebird this->cleanupTransaction = &Name::cloopcleanupTransactionDispatcher; this->deprecatedSetSequence = &Name::cloopdeprecatedSetSequenceDispatcher; this->setSequence2 = &Name::cloopsetSequence2Dispatcher; + this->flushSequences = &Name::cloopflushSequencesDispatcher; } } vTable; @@ -21465,6 +21480,20 @@ namespace Firebird } } + static void CLOOP_CARG cloopflushSequencesDispatcher(IReplicatedSession* self, IStatus* status) CLOOP_NOEXCEPT + { + StatusType status2(status); + + try + { + static_cast(self)->Name::flushSequences(&status2); + } + catch (...) + { + StatusType::catchException(&status2); + } + } + static void CLOOP_CARG cloopsetOwnerDispatcher(IPluginBase* self, IReferenceCounted* r) CLOOP_NOEXCEPT { try @@ -21534,6 +21563,7 @@ namespace Firebird virtual void cleanupTransaction(StatusType* status, ISC_INT64 number) = 0; virtual void deprecatedSetSequence(StatusType* status, const char* name, ISC_INT64 value) = 0; virtual void setSequence2(StatusType* status, const char* schemaName, const char* genName, ISC_INT64 value) = 0; + virtual void flushSequences(StatusType* status) = 0; }; template diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index eb0cfeae5a0..01d12c886ed 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -758,6 +758,7 @@ ISC_TIMESTAMP_TZ_EX = record IReplicatedSession_cleanupTransactionPtr = procedure(this: IReplicatedSession; status: IStatus; number: Int64); cdecl; IReplicatedSession_deprecatedSetSequencePtr = procedure(this: IReplicatedSession; status: IStatus; name: PAnsiChar; value: Int64); cdecl; IReplicatedSession_setSequence2Ptr = procedure(this: IReplicatedSession; status: IStatus; schemaName: PAnsiChar; genName: PAnsiChar; value: Int64); cdecl; + IReplicatedSession_flushSequencesPtr = procedure(this: IReplicatedSession; status: IStatus); cdecl; IProfilerPlugin_initPtr = procedure(this: IProfilerPlugin; status: IStatus; attachment: IAttachment; ticksFrequency: QWord); cdecl; IProfilerPlugin_startSessionPtr = function(this: IProfilerPlugin; status: IStatus; description: PAnsiChar; options: PAnsiChar; timestamp: ISC_TIMESTAMP_TZ): IProfilerSession; cdecl; IProfilerPlugin_flushPtr = procedure(this: IProfilerPlugin; status: IStatus); cdecl; @@ -3929,6 +3930,7 @@ ReplicatedSessionVTable = class(PluginBaseVTable) cleanupTransaction: IReplicatedSession_cleanupTransactionPtr; deprecatedSetSequence: IReplicatedSession_deprecatedSetSequencePtr; setSequence2: IReplicatedSession_setSequence2Ptr; + flushSequences: IReplicatedSession_flushSequencesPtr; end; IReplicatedSession = class(IPluginBase) @@ -3939,6 +3941,7 @@ IReplicatedSession = class(IPluginBase) procedure cleanupTransaction(status: IStatus; number: Int64); procedure deprecatedSetSequence(status: IStatus; name: PAnsiChar; value: Int64); procedure setSequence2(status: IStatus; schemaName: PAnsiChar; genName: PAnsiChar; value: Int64); + procedure flushSequences(status: IStatus); end; IReplicatedSessionImpl = class(IReplicatedSession) @@ -3953,6 +3956,7 @@ IReplicatedSessionImpl = class(IReplicatedSession) procedure cleanupTransaction(status: IStatus; number: Int64); virtual; abstract; procedure deprecatedSetSequence(status: IStatus; name: PAnsiChar; value: Int64); virtual; abstract; procedure setSequence2(status: IStatus; schemaName: PAnsiChar; genName: PAnsiChar; value: Int64); virtual; abstract; + procedure flushSequences(status: IStatus); virtual; abstract; end; ProfilerPluginVTable = class(PluginBaseVTable) @@ -10102,6 +10106,17 @@ procedure IReplicatedSession.setSequence2(status: IStatus; schemaName: PAnsiChar FbException.checkException(status); end; +procedure IReplicatedSession.flushSequences(status: IStatus); +begin + if (vTable.version < 5) then begin + FbException.setVersionError(status, 'IReplicatedSession', vTable.version, 5); + end + else begin + ReplicatedSessionVTable(vTable).flushSequences(Self, status); + end; + FbException.checkException(status); +end; + procedure IProfilerPlugin.init(status: IStatus; attachment: IAttachment; ticksFrequency: QWord); begin ProfilerPluginVTable(vTable).init(Self, status, attachment, ticksFrequency); @@ -17567,6 +17582,15 @@ procedure IReplicatedSessionImpl_setSequence2Dispatcher(this: IReplicatedSession end end; +procedure IReplicatedSessionImpl_flushSequencesDispatcher(this: IReplicatedSession; status: IStatus); cdecl; +begin + try + IReplicatedSessionImpl(this).flushSequences(status); + except + on e: Exception do FbException.catchException(status, e); + end +end; + var IReplicatedSessionImpl_vTable: ReplicatedSessionVTable; @@ -18956,6 +18980,7 @@ initialization IReplicatedSessionImpl_vTable.cleanupTransaction := @IReplicatedSessionImpl_cleanupTransactionDispatcher; IReplicatedSessionImpl_vTable.deprecatedSetSequence := @IReplicatedSessionImpl_deprecatedSetSequenceDispatcher; IReplicatedSessionImpl_vTable.setSequence2 := @IReplicatedSessionImpl_setSequence2Dispatcher; + IReplicatedSessionImpl_vTable.flushSequences := @IReplicatedSessionImpl_flushSequencesDispatcher; IProfilerPluginImpl_vTable := ProfilerPluginVTable.create; IProfilerPluginImpl_vTable.version := 4; diff --git a/src/jrd/replication/Publisher.cpp b/src/jrd/replication/Publisher.cpp index 3f5aa2b09be..34cac837d83 100644 --- a/src/jrd/replication/Publisher.cpp +++ b/src/jrd/replication/Publisher.cpp @@ -433,7 +433,15 @@ void REPL_trans_commit(thread_db* tdbb, jrd_tra* transaction) { const auto replicator = transaction->tra_replicator; if (!replicator) + { + const auto replicator = getReplicator(tdbb); + if (replicator) + { + FbLocalStatus status; + replicator->flushSequences(&status); + } return; + } FbLocalStatus status; replicator->commit(&status); @@ -452,7 +460,15 @@ void REPL_trans_rollback(thread_db* tdbb, jrd_tra* transaction) { const auto replicator = transaction->tra_replicator; if (!replicator) + { + const auto replicator = getReplicator(tdbb); + if (replicator) + { + FbLocalStatus status; + replicator->flushSequences(&status); + } return; + } FbLocalStatus status; replicator->rollback(&status); diff --git a/src/jrd/replication/Replicator.cpp b/src/jrd/replication/Replicator.cpp index a2e9f4b68db..bedf663f51a 100644 --- a/src/jrd/replication/Replicator.cpp +++ b/src/jrd/replication/Replicator.cpp @@ -207,18 +207,7 @@ void Replicator::commitTransaction(CheckStatusWrapper* status, Transaction* tran const auto dataLength = txnData.buffer->getCount() - sizeof(Block); fb_assert(txnData.flushes || dataLength > sizeof(UCHAR)); - for (const auto& generator : m_generators) - { - fb_assert(generator.name.object.hasData() && generator.name.schema.hasData()); - - const auto [schemaAtom, objectAtom] = txnData.defineQualifiedAtom(generator.name); - - txnData.putTag(opSetSequence); - txnData.putInt32(schemaAtom); - txnData.putInt32(objectAtom); - txnData.putInt64(generator.value); - } - + txnData.putGenerators(&m_generators); m_generators.clear(); txnData.putTag(opCommitTransaction); @@ -236,9 +225,13 @@ void Replicator::rollbackTransaction(CheckStatusWrapper* status, Transaction* tr { auto& txnData = transaction->getData(); - if (txnData.flushes) + if (txnData.flushes || m_generators.hasData()) { - txnData.putTag(opRollbackTransaction); + txnData.putGenerators(&m_generators); + m_generators.clear(); + + if (txnData.flushes) + txnData.putTag(opRollbackTransaction); flush(txnData, FLUSH_SYNC, BLOCK_END_TRANS); } } @@ -501,3 +494,26 @@ void Replicator::setSequence2(CheckStatusWrapper* status, ex.stuffException(status); } } + +void Replicator::flushSequences(CheckStatusWrapper* status) +{ + if (m_generators.isEmpty()) + return; + + try + { + BatchBlock block(getPool()); + block.header.traNumber = 0; + block.buffer = m_manager->getBuffer(); + block.header.length = (ULONG)block.buffer->getCount(); + + block.putGenerators(&m_generators); + m_generators.clear(); + + flush(block, FLUSH_SYNC); + } + catch (const Exception& ex) + { + ex.stuffException(status); + } +} diff --git a/src/jrd/replication/Replicator.h b/src/jrd/replication/Replicator.h index 38e0f0d01c0..7bbd9c12daa 100644 --- a/src/jrd/replication/Replicator.h +++ b/src/jrd/replication/Replicator.h @@ -42,6 +42,14 @@ namespace Replication typedef Firebird::ObjectsArray NameCache; typedef Firebird::HalfStaticArray SavepointStack; + struct GeneratorValue + { + Jrd::QualifiedName name; + SINT64 value = 0; + }; + + typedef Firebird::Array GeneratorCache; + struct BatchBlock { Block header{}; @@ -135,6 +143,21 @@ namespace Replication { buffer->add(data, length); } + + void putGenerators(const GeneratorCache* generators) + { + for (const auto& generator : *generators) + { + fb_assert(generator.name.object.hasData() && generator.name.schema.hasData()); + + const auto [schemaAtom, objectAtom] = defineQualifiedAtom(generator.name); + + putTag(opSetSequence); + putInt32(schemaAtom); + putInt32(objectAtom); + putInt64(generator.value); + } + } }; class Transaction final : @@ -255,14 +278,6 @@ namespace Replication BatchBlock m_data; }; - struct GeneratorValue - { - Jrd::QualifiedName name; - SINT64 value = 0; - }; - - typedef Firebird::Array GeneratorCache; - enum FlushReason { FLUSH_OVERFLOW, @@ -295,6 +310,8 @@ namespace Replication void setSequence2(Firebird::CheckStatusWrapper* status, const char* schemaName, const char* genName, SINT64 value) override; + void flushSequences(Firebird::CheckStatusWrapper* status) override; + private: Manager* const m_manager; const Config* const m_config; From b0c449dfd53f4005381a711ff5da665fb2ff0688 Mon Sep 17 00:00:00 2001 From: Andrey Kravchenko Date: Thu, 15 Jan 2026 09:53:25 +0300 Subject: [PATCH 2/4] Use att_replicator for flush sequences when commit or rollback without ReplicatedTransaction --- src/jrd/replication/Publisher.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/jrd/replication/Publisher.cpp b/src/jrd/replication/Publisher.cpp index 34cac837d83..18bee4ebd12 100644 --- a/src/jrd/replication/Publisher.cpp +++ b/src/jrd/replication/Publisher.cpp @@ -434,11 +434,11 @@ void REPL_trans_commit(thread_db* tdbb, jrd_tra* transaction) const auto replicator = transaction->tra_replicator; if (!replicator) { - const auto replicator = getReplicator(tdbb); - if (replicator) + const auto att = tdbb->getAttachment(); + if (att->att_replicator) { FbLocalStatus status; - replicator->flushSequences(&status); + att->att_replicator->flushSequences(&status); } return; } @@ -461,11 +461,11 @@ void REPL_trans_rollback(thread_db* tdbb, jrd_tra* transaction) const auto replicator = transaction->tra_replicator; if (!replicator) { - const auto replicator = getReplicator(tdbb); - if (replicator) + const auto att = tdbb->getAttachment(); + if (att->att_replicator) { FbLocalStatus status; - replicator->flushSequences(&status); + att->att_replicator->flushSequences(&status); } return; } From ecea53c2e9b359ded6b768f58ecf508512506aca Mon Sep 17 00:00:00 2001 From: Andrey Kravchenko Date: Tue, 24 Feb 2026 17:38:53 +0300 Subject: [PATCH 3/4] Create tra_replicator when change generator value without any operations in transaction before This allows to remove the new method from the replication API --- examples/replication/fbSampleReplicator.cpp | 7 ----- src/include/firebird/FirebirdInterface.idl | 1 - src/include/firebird/IdlFbInterfaces.h | 30 --------------------- src/include/gen/Firebird.pas | 25 ----------------- src/jrd/dpm.epp | 2 +- src/jrd/replication/Publisher.cpp | 26 ++++++------------ src/jrd/replication/Publisher.h | 2 +- src/jrd/replication/Replicator.cpp | 23 ---------------- src/jrd/replication/Replicator.h | 2 -- 9 files changed, 10 insertions(+), 108 deletions(-) diff --git a/examples/replication/fbSampleReplicator.cpp b/examples/replication/fbSampleReplicator.cpp index fa62653c48e..8eac456b053 100644 --- a/examples/replication/fbSampleReplicator.cpp +++ b/examples/replication/fbSampleReplicator.cpp @@ -37,7 +37,6 @@ class ReplPlugin : public IReplicatedSessionImpl FB_BOOLEAN cleanupTransaction(ISC_INT64 number) override; FB_BOOLEAN deprecatedSetSequence(const char* name, ISC_INT64 value) override; FB_BOOLEAN setSequence2(const char* schemaName, const char* genName, ISC_INT64 value) override; - FB_BOOLEAN flushSequences() override; private: friend class ReplTransaction; @@ -265,12 +264,6 @@ FB_BOOLEAN ReplPlugin::setSequence2(const char* schemaName, const char* genName, return FB_TRUE; } -FB_BOOLEAN ReplPlugin::flushSequences() -{ - WriteLog(log, "%p\tflushSequences()\n"); - return FB_TRUE; -} - ReplTransaction::ReplTransaction(ReplPlugin* session, ITransaction* transaction, ISC_INT64 number): parent(session), trans(transaction) { diff --git a/src/include/firebird/FirebirdInterface.idl b/src/include/firebird/FirebirdInterface.idl index ff4a644ed8a..4bce83e70a4 100644 --- a/src/include/firebird/FirebirdInterface.idl +++ b/src/include/firebird/FirebirdInterface.idl @@ -1830,7 +1830,6 @@ interface ReplicatedSession : PluginBase version: // 5.0 => 6.0 Alpha1 void setSequence2(Status status, const string schemaName, const string genName, int64 value); - void flushSequences(Status status); } // Profiler interfaces diff --git a/src/include/firebird/IdlFbInterfaces.h b/src/include/firebird/IdlFbInterfaces.h index dab643ff108..ea71d25e6bc 100644 --- a/src/include/firebird/IdlFbInterfaces.h +++ b/src/include/firebird/IdlFbInterfaces.h @@ -7328,7 +7328,6 @@ namespace Firebird void (CLOOP_CARG *cleanupTransaction)(IReplicatedSession* self, IStatus* status, ISC_INT64 number) CLOOP_NOEXCEPT; void (CLOOP_CARG *deprecatedSetSequence)(IReplicatedSession* self, IStatus* status, const char* name, ISC_INT64 value) CLOOP_NOEXCEPT; void (CLOOP_CARG *setSequence2)(IReplicatedSession* self, IStatus* status, const char* schemaName, const char* genName, ISC_INT64 value) CLOOP_NOEXCEPT; - void (CLOOP_CARG *flushSequences)(IReplicatedSession* self, IStatus* status) CLOOP_NOEXCEPT; }; protected: @@ -7386,19 +7385,6 @@ namespace Firebird static_cast(this->cloopVTable)->setSequence2(this, status, schemaName, genName, value); StatusType::checkException(status); } - - template void flushSequences(StatusType* status) - { - if (cloopVTable->version < 5) - { - StatusType::setVersionError(status, "IReplicatedSession", cloopVTable->version, 5); - StatusType::checkException(status); - return; - } - StatusType::clearException(status); - static_cast(this->cloopVTable)->flushSequences(this, status); - StatusType::checkException(status); - } }; #define FIREBIRD_IPROFILER_PLUGIN_VERSION 4u @@ -21401,7 +21387,6 @@ namespace Firebird this->cleanupTransaction = &Name::cloopcleanupTransactionDispatcher; this->deprecatedSetSequence = &Name::cloopdeprecatedSetSequenceDispatcher; this->setSequence2 = &Name::cloopsetSequence2Dispatcher; - this->flushSequences = &Name::cloopflushSequencesDispatcher; } } vTable; @@ -21480,20 +21465,6 @@ namespace Firebird } } - static void CLOOP_CARG cloopflushSequencesDispatcher(IReplicatedSession* self, IStatus* status) CLOOP_NOEXCEPT - { - StatusType status2(status); - - try - { - static_cast(self)->Name::flushSequences(&status2); - } - catch (...) - { - StatusType::catchException(&status2); - } - } - static void CLOOP_CARG cloopsetOwnerDispatcher(IPluginBase* self, IReferenceCounted* r) CLOOP_NOEXCEPT { try @@ -21563,7 +21534,6 @@ namespace Firebird virtual void cleanupTransaction(StatusType* status, ISC_INT64 number) = 0; virtual void deprecatedSetSequence(StatusType* status, const char* name, ISC_INT64 value) = 0; virtual void setSequence2(StatusType* status, const char* schemaName, const char* genName, ISC_INT64 value) = 0; - virtual void flushSequences(StatusType* status) = 0; }; template diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index 01d12c886ed..eb0cfeae5a0 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -758,7 +758,6 @@ ISC_TIMESTAMP_TZ_EX = record IReplicatedSession_cleanupTransactionPtr = procedure(this: IReplicatedSession; status: IStatus; number: Int64); cdecl; IReplicatedSession_deprecatedSetSequencePtr = procedure(this: IReplicatedSession; status: IStatus; name: PAnsiChar; value: Int64); cdecl; IReplicatedSession_setSequence2Ptr = procedure(this: IReplicatedSession; status: IStatus; schemaName: PAnsiChar; genName: PAnsiChar; value: Int64); cdecl; - IReplicatedSession_flushSequencesPtr = procedure(this: IReplicatedSession; status: IStatus); cdecl; IProfilerPlugin_initPtr = procedure(this: IProfilerPlugin; status: IStatus; attachment: IAttachment; ticksFrequency: QWord); cdecl; IProfilerPlugin_startSessionPtr = function(this: IProfilerPlugin; status: IStatus; description: PAnsiChar; options: PAnsiChar; timestamp: ISC_TIMESTAMP_TZ): IProfilerSession; cdecl; IProfilerPlugin_flushPtr = procedure(this: IProfilerPlugin; status: IStatus); cdecl; @@ -3930,7 +3929,6 @@ ReplicatedSessionVTable = class(PluginBaseVTable) cleanupTransaction: IReplicatedSession_cleanupTransactionPtr; deprecatedSetSequence: IReplicatedSession_deprecatedSetSequencePtr; setSequence2: IReplicatedSession_setSequence2Ptr; - flushSequences: IReplicatedSession_flushSequencesPtr; end; IReplicatedSession = class(IPluginBase) @@ -3941,7 +3939,6 @@ IReplicatedSession = class(IPluginBase) procedure cleanupTransaction(status: IStatus; number: Int64); procedure deprecatedSetSequence(status: IStatus; name: PAnsiChar; value: Int64); procedure setSequence2(status: IStatus; schemaName: PAnsiChar; genName: PAnsiChar; value: Int64); - procedure flushSequences(status: IStatus); end; IReplicatedSessionImpl = class(IReplicatedSession) @@ -3956,7 +3953,6 @@ IReplicatedSessionImpl = class(IReplicatedSession) procedure cleanupTransaction(status: IStatus; number: Int64); virtual; abstract; procedure deprecatedSetSequence(status: IStatus; name: PAnsiChar; value: Int64); virtual; abstract; procedure setSequence2(status: IStatus; schemaName: PAnsiChar; genName: PAnsiChar; value: Int64); virtual; abstract; - procedure flushSequences(status: IStatus); virtual; abstract; end; ProfilerPluginVTable = class(PluginBaseVTable) @@ -10106,17 +10102,6 @@ procedure IReplicatedSession.setSequence2(status: IStatus; schemaName: PAnsiChar FbException.checkException(status); end; -procedure IReplicatedSession.flushSequences(status: IStatus); -begin - if (vTable.version < 5) then begin - FbException.setVersionError(status, 'IReplicatedSession', vTable.version, 5); - end - else begin - ReplicatedSessionVTable(vTable).flushSequences(Self, status); - end; - FbException.checkException(status); -end; - procedure IProfilerPlugin.init(status: IStatus; attachment: IAttachment; ticksFrequency: QWord); begin ProfilerPluginVTable(vTable).init(Self, status, attachment, ticksFrequency); @@ -17582,15 +17567,6 @@ procedure IReplicatedSessionImpl_setSequence2Dispatcher(this: IReplicatedSession end end; -procedure IReplicatedSessionImpl_flushSequencesDispatcher(this: IReplicatedSession; status: IStatus); cdecl; -begin - try - IReplicatedSessionImpl(this).flushSequences(status); - except - on e: Exception do FbException.catchException(status, e); - end -end; - var IReplicatedSessionImpl_vTable: ReplicatedSessionVTable; @@ -18980,7 +18956,6 @@ initialization IReplicatedSessionImpl_vTable.cleanupTransaction := @IReplicatedSessionImpl_cleanupTransactionDispatcher; IReplicatedSessionImpl_vTable.deprecatedSetSequence := @IReplicatedSessionImpl_deprecatedSetSequenceDispatcher; IReplicatedSessionImpl_vTable.setSequence2 := @IReplicatedSessionImpl_setSequence2Dispatcher; - IReplicatedSessionImpl_vTable.flushSequences := @IReplicatedSessionImpl_flushSequencesDispatcher; IProfilerPluginImpl_vTable := ProfilerPluginVTable.create; IProfilerPluginImpl_vTable.version := 4; diff --git a/src/jrd/dpm.epp b/src/jrd/dpm.epp index 94c01d8564f..ec7d5964f6b 100644 --- a/src/jrd/dpm.epp +++ b/src/jrd/dpm.epp @@ -1440,7 +1440,7 @@ SINT64 DPM_gen_id(thread_db* tdbb, SLONG generator, bool initialize, SINT64 val) if (transaction) transaction->tra_flags |= TRA_write; - REPL_gen_id(tdbb, generator, value); + REPL_gen_id(tdbb, generator, value, transaction); return value; } diff --git a/src/jrd/replication/Publisher.cpp b/src/jrd/replication/Publisher.cpp index 18bee4ebd12..d938b642a13 100644 --- a/src/jrd/replication/Publisher.cpp +++ b/src/jrd/replication/Publisher.cpp @@ -433,15 +433,7 @@ void REPL_trans_commit(thread_db* tdbb, jrd_tra* transaction) { const auto replicator = transaction->tra_replicator; if (!replicator) - { - const auto att = tdbb->getAttachment(); - if (att->att_replicator) - { - FbLocalStatus status; - att->att_replicator->flushSequences(&status); - } return; - } FbLocalStatus status; replicator->commit(&status); @@ -460,15 +452,7 @@ void REPL_trans_rollback(thread_db* tdbb, jrd_tra* transaction) { const auto replicator = transaction->tra_replicator; if (!replicator) - { - const auto att = tdbb->getAttachment(); - if (att->att_replicator) - { - FbLocalStatus status; - att->att_replicator->flushSequences(&status); - } return; - } FbLocalStatus status; replicator->rollback(&status); @@ -638,7 +622,7 @@ void REPL_erase(thread_db* tdbb, const record_param* rpb, jrd_tra* transaction) checkStatus(tdbb, status, transaction); } -void REPL_gen_id(thread_db* tdbb, SLONG genId, SINT64 value) +void REPL_gen_id(thread_db* tdbb, SLONG genId, SINT64 value, jrd_tra* transaction) { if (tdbb->tdbb_flags & (TDBB_dont_post_dfw | TDBB_repl_in_progress)) return; @@ -657,6 +641,13 @@ void REPL_gen_id(thread_db* tdbb, SLONG genId, SINT64 value) if (!replicator) return; + FbLocalStatus status; + + // Create IReplicatedTransaction object for current transaction + // without any operations before changing generator + if (transaction && !transaction->tra_replicator) + getReplicator(tdbb, status, transaction); + const auto attachment = tdbb->getAttachment(); QualifiedName genName; @@ -670,7 +661,6 @@ void REPL_gen_id(thread_db* tdbb, SLONG genId, SINT64 value) AutoSetRestoreFlag noRecursion(&tdbb->tdbb_flags, TDBB_repl_in_progress, true); - FbLocalStatus status; replicator->setSequence2(&status, genName.schema.c_str(), genName.object.c_str(), value); checkStatus(tdbb, status); diff --git a/src/jrd/replication/Publisher.h b/src/jrd/replication/Publisher.h index 7fee6ceee1c..c69d25215b9 100644 --- a/src/jrd/replication/Publisher.h +++ b/src/jrd/replication/Publisher.h @@ -44,7 +44,7 @@ void REPL_store(Jrd::thread_db* tdbb, const Jrd::record_param* rpb, void REPL_modify(Jrd::thread_db* tdbb, const Jrd::record_param* orgRpb, const Jrd::record_param* newRpb, Jrd::jrd_tra* transaction); void REPL_erase(Jrd::thread_db* tdbb, const Jrd::record_param* rpb, Jrd::jrd_tra* transaction); -void REPL_gen_id(Jrd::thread_db* tdbb, SLONG genId, SINT64 value); +void REPL_gen_id(Jrd::thread_db* tdbb, SLONG genId, SINT64 value, Jrd::jrd_tra* transaction); void REPL_exec_sql(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction, const Firebird::string& sql, const Firebird::ObjectsArray& schemaSearchPath); void REPL_journal_switch(Jrd::thread_db* tdbb); diff --git a/src/jrd/replication/Replicator.cpp b/src/jrd/replication/Replicator.cpp index bedf663f51a..7b4773259c9 100644 --- a/src/jrd/replication/Replicator.cpp +++ b/src/jrd/replication/Replicator.cpp @@ -494,26 +494,3 @@ void Replicator::setSequence2(CheckStatusWrapper* status, ex.stuffException(status); } } - -void Replicator::flushSequences(CheckStatusWrapper* status) -{ - if (m_generators.isEmpty()) - return; - - try - { - BatchBlock block(getPool()); - block.header.traNumber = 0; - block.buffer = m_manager->getBuffer(); - block.header.length = (ULONG)block.buffer->getCount(); - - block.putGenerators(&m_generators); - m_generators.clear(); - - flush(block, FLUSH_SYNC); - } - catch (const Exception& ex) - { - ex.stuffException(status); - } -} diff --git a/src/jrd/replication/Replicator.h b/src/jrd/replication/Replicator.h index 7bbd9c12daa..0c222132aa5 100644 --- a/src/jrd/replication/Replicator.h +++ b/src/jrd/replication/Replicator.h @@ -310,8 +310,6 @@ namespace Replication void setSequence2(Firebird::CheckStatusWrapper* status, const char* schemaName, const char* genName, SINT64 value) override; - void flushSequences(Firebird::CheckStatusWrapper* status) override; - private: Manager* const m_manager; const Config* const m_config; From 488667a50c60110a6af0aa5f8384c3b11edd4e49 Mon Sep 17 00:00:00 2001 From: Andrey Kravchenko Date: Wed, 25 Feb 2026 09:21:00 +0300 Subject: [PATCH 4/4] Change pass generators by reference - thanks to Dmitry --- src/jrd/replication/Replicator.cpp | 4 ++-- src/jrd/replication/Replicator.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/jrd/replication/Replicator.cpp b/src/jrd/replication/Replicator.cpp index 7b4773259c9..e0ee95027e5 100644 --- a/src/jrd/replication/Replicator.cpp +++ b/src/jrd/replication/Replicator.cpp @@ -207,7 +207,7 @@ void Replicator::commitTransaction(CheckStatusWrapper* status, Transaction* tran const auto dataLength = txnData.buffer->getCount() - sizeof(Block); fb_assert(txnData.flushes || dataLength > sizeof(UCHAR)); - txnData.putGenerators(&m_generators); + txnData.putGenerators(m_generators); m_generators.clear(); txnData.putTag(opCommitTransaction); @@ -227,7 +227,7 @@ void Replicator::rollbackTransaction(CheckStatusWrapper* status, Transaction* tr if (txnData.flushes || m_generators.hasData()) { - txnData.putGenerators(&m_generators); + txnData.putGenerators(m_generators); m_generators.clear(); if (txnData.flushes) diff --git a/src/jrd/replication/Replicator.h b/src/jrd/replication/Replicator.h index 0c222132aa5..6bbce609af7 100644 --- a/src/jrd/replication/Replicator.h +++ b/src/jrd/replication/Replicator.h @@ -144,9 +144,9 @@ namespace Replication buffer->add(data, length); } - void putGenerators(const GeneratorCache* generators) + void putGenerators(const GeneratorCache& generators) { - for (const auto& generator : *generators) + for (const auto& generator : generators) { fb_assert(generator.name.object.hasData() && generator.name.schema.hasData());