Skip to content

Conversation

@akostadinov
Copy link
Contributor

@akostadinov akostadinov commented Dec 10, 2025

Improve oracle performance by

  • disabling array fetch for queries with lobs
  • fetch lobs using piecewise fetch LONG interface
  • clean-up prepared statements when connection returns to the pool to free memory

Also added a couple of small fixes.

Supersedes #4176

ActiveSupport.on_load(:active_record) do
if System::Database.oracle?
require 'arel/visitors/oracle12_hack'
require 'arel/visitors/oracle12_hack' || next # once done, we can skip setup
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe on application reload we don't need any of this code to be executed again. Using this require statement as a state tracking tool know whether we ever run or not. If your local development environment breaks on code reload, this would be the culprit. Be my guests any time!


After do
ActiveRecord::Base.clear_active_connections!
ActiveRecord::Base.connection_handler.clear_active_connections!
Copy link
Contributor Author

Choose a reason for hiding this comment

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

avoid DEPRECATION warning

# NOTE: Use ENV['DB'] only to install oracle dependencies
group :oracle do
oracle = -> { (ENV['ORACLE'] == '1') || ENV.fetch('DATABASE_URL', ENV['DB'])&.start_with?('oracle') }
ENV['NLS_LANG'] ||= 'AMERICAN_AMERICA.UTF8' if oracle
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Another attempt at setting reliably NLS_LANG before oracle connection is established and only if using oracle. Might be a hack but I'm not sure any other place would be less of a hack and I'm tired of trying to figure out where that other place might be. So here I'm sure it's gonna be set prior loading the oracle gems.

Copy link
Contributor

Choose a reason for hiding this comment

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

Did we have encoding problems before this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You can see always a complaint that we fallback to some 7bit ASCII encoding if you don't set this variable separately in your environment. But we have always tried to set a default in the initializer. But that has been too late. So this one should be early enough always.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

you can see that line removed from the initializer

@qltysh
Copy link

qltysh bot commented Dec 15, 2025

❌ 9 blocking issues (9 total)

Tool Category Rule Count
reek Lint OracleLobLargeUpdateTest has the variable name 'large_policy_data_v2' 3
reek Lint OracleLobLargeUpdateTest#generate_large_policies_config calls '[test_policy].to_json' 2 times 2
rubocop Style Incorrect formatting, autoformat by running qlty fmt. 1
rubocop Lint Models should subclass ApplicationRecord. 1
rubocop Style Avoid using OpenStruct; use Struct, Hash, a class or test doubles instead. 1
rubocop Style Surrounding space missing in default value assignment. 1

@qltysh one-click actions:

  • Auto-fix formatting (qlty fmt && git push)

Copy link
Contributor

@jlledom jlledom left a comment

Choose a reason for hiding this comment

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

I would ask you to add better description to complicated PRs like this. The amount of investigation required to understand any single line in that oracle initializer is big enough for the PR to not be reviewable in practice. Now you have claude, you can tell it to explain what you did, and verify it's correct before pasting it in the PR description.

Also, creating one single commit for each conceptually different work would make reviewer's life easier as well.

# NOTE: Use ENV['DB'] only to install oracle dependencies
group :oracle do
oracle = -> { (ENV['ORACLE'] == '1') || ENV.fetch('DATABASE_URL', ENV['DB'])&.start_with?('oracle') }
ENV['NLS_LANG'] ||= 'AMERICAN_AMERICA.UTF8' if oracle
Copy link
Contributor

Choose a reason for hiding this comment

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

Did we have encoding problems before this?

jlledom
jlledom previously approved these changes Dec 18, 2025
@jlledom
Copy link
Contributor

jlledom commented Dec 19, 2025

See the errors in the Oracle cucumbers:

Message:could not obtain a connection from the pool within 5.000 seconds (waited 5.000 seconds); all pooled connections were in use (ActiveRecord::ConnectionTimeoutError)

They might be related to the changes 🤕.

@akostadinov
Copy link
Contributor Author

They might be related to the changes 🤕.

Could be, although I have no idea what might be the case as we don't change the connection management. I wonder if this closing of cursors might take too long sometimes. But then, that can also explain issues we have previously seen with random slowdowns. Will need to try with and without it and then whateever else needed. This is a nightmare.

@jlledom
Copy link
Contributor

jlledom commented Jan 7, 2026

They might be related to the changes 🤕.

Could be, although I have no idea what might be the case as we don't change the connection management. I wonder if this closing of cursors might take too long sometimes. But then, that can also explain issues we have previously seen with random slowdowns. Will need to try with and without it and then whateever else needed. This is a nightmare.

My thoughts are with you

def close_and_clear_statements
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
statement_count = @statements&.length || 0
@statements&.clear
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some AI explanation why clearing cursors from app end may not be much of a performance penalty because oracle server still caches these for reuse.

Why Oracle Keeps "Closed" Cursors

Oracle's V$OPEN_CURSOR view is somewhat misleadingly named - it shows cursors Oracle is tracking for the session, not just actively open ones. Oracle keeps them for performance:

  1. Session Cursor Cache: When you "close" a cursor from the application side, Oracle often keeps it cached in case you run the same SQL again. This is controlled by the SESSION_CACHED_CURSORS parameter.
  2. Soft vs Hard Close:
    - Soft close: Application closes the cursor, but Oracle keeps it in its cache
    - Hard close: Oracle actually deallocates it

@akostadinov
Copy link
Contributor Author

@jlledom , now I'm not sure the changes are related to the failures. I can't reproduce the exact errors but other failure types randomly appear.

Sign up for free to 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.

3 participants