Skip to content

Memory leak after upgrading from 3.12.6 to 3.13.1 #4621

@vasuadari

Description

@vasuadari

Elixir version

1.17

Database and Version

postgres:17.3-alpine3.21

Ecto Versions

3.13.1

Database Adapter and Versions (postgrex, myxql, etc)

postgrex: 0.20.0

Current behavior

While trying to upgrade elixir version from 1.16 to 1.17 & Erlang from 26 to 27. Mix compile getting stuck at two files,

Compiled lib/xyz/finances/securities/mutual_fund/fund_category/etl.ex
Compiled lib/xyz/finances/securities/mutual_fund/entity_transfer_agent/etl.ex
Compiled lib/xyz/finances/securities/mutual_fund/amc_aum/etl.ex
Compiled lib/xyz/finances/securities/mutual_fund/apus_fund_scheme/etl.ex
Compiled lib/xyz/finances/insurance/product/etl.ex
Compiled lib/xyz/finances/insurance/provider/etl.ex
Compiled lib/xyz/bse/equity/etl.ex
Compiled lib/xyz/bse/equity_nav/etl.ex
Compiled lib/xyz/finances/securities/mutual_fund/amc/etl.ex



Compiling lib/xyz/finances/securities/mutual_fund/fund_plan/etl/resource.ex (it's taking more than 10s)
Compiling lib/xyz/finances/securities/mutual_fund/fund/etl/resource.ex (it's taking more than 10s).

System memory shots up completely and my terminal gets killed. I was suspecting Erlang version, reverting it didn't help. But after reverting Ecto to previous version, problem got solved.

Files which were stuck has complex Ecto queries, below is the sample

defmodule AnonymizedQuery do
  import Ecto.Query

  @default_date ~D[2018-01-31]

  def build_catalog_query do
    # Subqueries for optimization
    sub_entity_codes =
      SubEntityCode
      |> select([
        :code,
        :name,
        :parent_entity_id,
        :entity_id,
        :online_code,
        :sub_entity_id
      ])

    sub_entity_identifiers = select(SubEntityIdentifier, [:sub_entity_code, :identifier_code, :reinvestment_flag])

    entity_metrics =
      order_by(EntityMetric, desc: :entity_id, desc: :as_on_date)
      |> distinct(:entity_id)

    price_as_on = from(price in Price, where: price.price_date == ^@default_date)

    # Main query with extensive joins
    MainEntity
    |> join(:inner, [me], se in SubEntity, on: se.entity_id == me.entity_id)
    |> join(:left, [me], lp in LatestPrice, on: lp.main_entity_id == me.entity_id)
    |> join(:left, [_me, se], sei in subquery(sub_entity_identifiers),
      on: sei.sub_entity_code == se.sub_entity_id
    )
    |> join(:left, [_me, se], sec in subquery(sub_entity_codes),
      on: sec.sub_entity_id == se.sub_entity_id
    )
    |> join(:left, [me], provider in Provider, on: provider.provider_id == me.provider_id)
    |> join(:left, [_me, se], td in TransactionDetail, on: td.sub_entity_id == se.sub_entity_id)
    |> join(:left, [_me, _se, _lp, sei], score in ResearchScore,
      on: score.identifier_code == sei.identifier_code
    )
    |> join(:left, [me], em in subquery(entity_metrics), on: em.entity_id == me.entity_id)
    |> join(:left, [me], sp in ServiceProvider, on: sp.provider_id == me.provider_id)
    |> join(:left, [_me, _se, _lp, _sei, sec], rta in ExternalEntityA,
      on: rta.code == sec.code
    )
    |> join(:left, [_me, _se, _lp, sei], isin in ExternalEntityB,
      on: isin.identifier_code == sei.identifier_code
    )
    |> join(:left, [me], erl in EntityReturnLatest, on: erl.entity_id == me.entity_id)
    |> join(:left, [me], er in EntityRating, on: er.entity_id == me.entity_id)
    |> join(:left, [me], ec in EntityCategory, on: ec.category_id == me.category_id)
    |> join(:left, [_me, _se, _lp, sei], exchange_a in ExchangeA, on: sei.identifier_code == exchange_a.identifier_code)
    |> join(:left, [_me, _se, _lp, sei], exchange_b in ExchangeB, on: sei.identifier_code == exchange_b.identifier_code)
    |> join(:left, [me], price_h in subquery(price_as_on), on: price_h.main_entity_id == me.entity_id)
    |> join(:left, [me], tax in EntityTaxation, on: tax.entity_id == me.entity_id)
    |> join(:left, [_me, _se, _lp, sei], bse in ExchangeMaster,
      on: bse.identifier == sei.identifier_code
    )
    |> distinct([_me, se], se.sub_entity_id)
    |> select(
      [me],
      map(me, [
        :entity_id,
        :provider_id,
        :external_code,
        :basic_name,
        :entity_name,
        :plan_name,
        :category_id,
        :color,
        :dividend_periodicity,
        :is_dividend,
        :is_direct_plan,
        :min_balance,
        :min_initial_investment,
        :min_investment_multiples,
        :min_subsequent_investment,
        :min_subsequent_investment_unit,
        :min_subsequent_sip_investment,
        :min_swp_withdrawal,
        :min_withdrawal_unit,
        :min_withdrawal_multiple_amount,
        :min_withdrawal_amount,
        :max_investment_amount,
        :minor_investments_allowed,
        :status,
        :regulatory_category_name,
        :regulatory_primary_category_name,
        :transfer_agent_short_name,
        :transaction_status,
        :type_id,
        :updated_at,
        :new_entity,
        :is_etf_entity,
        :issue_actual_close,
        :issue_open,
        :entity_expense
      ])
    )
    |> select_merge(
      [_me, se],
      map(se, [:sub_entity_id, :sub_entity_name, :sub_entity_full_name, :description])
    )
    |> select_merge([_me, _se, lp], map(lp, [:price, :price_date]))
    |> select_merge([_me, _se, _lp, sei], map(sei, [:identifier_code, :reinvestment_flag]))
    |> select_merge(
      [_me, _se, _lp, _sei, sec],
      map(sec, [
        :parent_entity_id,
        :entity_id,
        :code
      ])
    )
    |> select_merge(
      [_me, _se, _lp, _sei, _sec, provider],
      map(provider, [
        :provider_full_name,
        :provider_short_name
      ])
    )
    |> select_merge(
      [_me, _se, _lp, _sei, _sec, _provider, td],
      map(td, [
        :sip,
        :swp,
        :stp,
        :is_investment_allowed,
        :is_withdrawal_allowed,
        :is_sip_allowed,
        :is_swp_allowed,
        :is_stp_allowed,
        :is_switch_in_allowed,
        :is_switch_out_allowed
      ])
    )
    |> select_merge(
      [_me, _se, _lp, _sei, _sec, _provider, _td, score],
      map(score, [
        :entity_aggregate_score,
        :recommended_status,
        :entity_recommendation_rating,
        :portfolio_audit_ic_blacklist,
        :research_sub_asset_class_code
      ])
    )
    |> select_merge(
      [_me, _se, _lp, _sei, _sec, _provider, _td, _score, em],
      map(em, [:metric_value, :as_on_date])
    )
    |> select_merge(
      [_me, _se, _lp, _sei, _sec, _provider, _td, _score, _em, sp],
      map(sp, [:provider_code, :provider_slug, :purchase, :logo, :icon, :is_region_supported, :other_url])
    )
    |> select_merge(
      [_me, _se, _lp, _sei, _sec, _provider, _td, _score, _em, _sp, rta, isin],
      map(rta, [:slug])
    )
    |> select_merge(
      [_me, _se, _lp, _sei, _sec, _provider, _td, _score, _em, _sp, rta, isin],
      %{
        rta_entity_slug: rta.slug,
        isin_entity_slug: isin.slug,
        rta_entity_scheme_id: rta.entity_scheme_id,
        isin_entity_scheme_id: isin.entity_scheme_id
      }
    )
    |> select_merge(
      [_me, _se, _lp, _sei, _sec, _provider, _td, _score, _em, _sp, _rta, _isin, erl, er],
      %{
        return_five_years: erl.return_5year,
        return_10year: erl.return_10year,
        return_1day: erl.return_1day,
        return_1month: erl.return_1month,
        return_1week: erl.return_1week,
        return_1year: erl.return_1year,
        return_2year: erl.return_2year,
        return_3month: erl.return_3month,
        return_3year: erl.return_3year,
        return_4year: erl.return_4year,
        return_5year: erl.return_5year,
        return_6month: erl.return_6month,
        return_7year: erl.return_7year,
        return_9month: erl.return_9month,
        return_since_launch: erl.return_since_launch,
        return_ytd: erl.return_ytd,
        risk_level: er.risk_grade
      }
    )
    |> select_merge(
      [_me, _se, _lp, _sei, _sec, _provider, _td, _score, _em, _sp, _rta, _isin, _erl, _er, ec],
      map(ec, [:category_id, :category_name])
    )
    |> select_merge(
      [_me, _se, _lp, _sei, _sec, _provider, _td, _score, _em, _sp, _rta, _isin, _erl, _er, _ec, exchange_a],
      %{
        exchange_a_scheme_code: exchange_a.scheme_code,
        exchange_a_provider_code: exchange_a.provider_code
      }
    )
    |> select_merge(
      [
        _me,
        _se,
        _lp,
        _sei,
        _sec,
        _provider,
        _td,
        _score,
        _em,
        _sp,
        _rta,
        _isin,
        _erl,
        _er,
        _ec,
        _exchange_a,
        exchange_b
      ],
      %{
        exchange_b_product_code: exchange_b.product_code
      }
    )
    |> select_merge(
      [
        _me,
        _se,
        _lp,
        _sei,
        _sec,
        _provider,
        _td,
        _score,
        _em,
        _sp,
        _rta,
        _isin,
        _erl,
        _er,
        _ec,
        _exchange_a,
        _exchange_b,
        price_h
      ],
      %{
        price_on_reference_date: price_h.price
      }
    )
    |> select_merge(
      [
        _me,
        _se,
        _lp,
        _sei,
        _sec,
        _provider,
        _td,
        _score,
        _em,
        _sp,
        _rta,
        _isin,
        _erl,
        _er,
        _ec,
        _exchange_a,
        _exchange_b,
        _price_h,
        tax
      ],
      %{tax_allocation: tax.allocation}
    )
    |> select_merge(
      [
        _me,
        _se,
        _lp,
        _sei,
        _sec,
        _provider,
        _td,
        _score,
        _em,
        _sp,
        _rta,
        _isin,
        _erl,
        _er,
        _ec,
        _exchange_a,
        _exchange_b,
        _price_h,
        _tax,
        bse
      ],
      %{bse_scheme_code: bse.scheme_code}
    )
  end
end

Expected behavior

Using up all memory definitely should be prevented by having some circuit breaker mechanism.

I was not able to debug the exact reason. I'll try to spend sometime this week by looking at change to zero-in on exact issue. Please let me know if you need any other details.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions