Skip to content

Prepared statements issue (continued) #97

@jeroenbourgois

Description

@jeroenbourgois

TDLR: the prepare: :unnamed setting seems to have no effect.

We have read through related tickets like #92 and #83 but still want to create a new one.

Our use case is as follows: users can upload xlsx files that contain a large number of rows (+3000) to process. Processing in our case means doing some data cleansing and then insert and or update multiple records across tables.

To execute the queries we use the regular Ecto API, being all/2, one/2, update/2, etc. For each row to be processed, we execute 10 to 15 queries, depending on the flow.

Whilst developing we always used stub files with a very limited amount of rows. Now the client is using actual files with said number of rows, meaning that the number of executed queries quickly ramps up.

Upon the first report of a crash and after seeing the uploaded file, we quickly reasoned that it probably was a timeout on the GET request or something like that. Instead, we saw the error described in the other related issue reports:

MyXQL.Connection (#PID<0.2375.0>) disconnected: ** (DBConnection.ConnectionError) client #PID<0.2952.0> exited

After that, we ran the process again whilst looking at the :observer and grabbed the messages from the exited PID, where we could read:

#{'__struct__' => 'Elixir.MyXQL.Connection',
  client =>
      #{'__struct__' => 'Elixir.MyXQL.Client',connection_id => 2275,
        sock => {gen_tcp,#Port<0.129>}},
  cursors => #{},
  disconnect_on_error_codes => ['ER_MAX_PREPARED_STMT_COUNT_REACHED'],
  last_ref => nil,ping_timeout => 15000,prepare => unnamed,
  queries => #Ref<0.3597155187.3075080195.95704>,transaction_status => idle}

A bit surprising, since we did not realize we were using prepared statements, but it made perfect sense, considering the amount of queries. We ran it again and while running kept an eye on the server stats for the prepared statement count and although it increased, it was never near the maximum allowed on the server...

Still, it kind of made sense as a possible root cause. We then first tried chunking all rows into smaller lists and processing those with a Process.sleep/1 after each row, to allow the connections to be cleaned up, but the error persisted.

We also tried applying the prepare: :unnamed in our config, but that seemed to do 'nothing' (as in: the count of prepared statements went up none the less). Here is how we provided the option inside config.exs

config :the_app, TheApp.Repo,
  username: System.get_env("DB_USER"),
  password: System.get_env("DB_PASSWORD"),
  database: System.get_env("DB_NAME"),
  hostname: System.get_env("DB_HOST"),
  pool_size: 5,
  pool_overflow: 20,
  timeout: 120_000,
  prepare: :unnamed

As you can see we also tried playing with the pool_size and the pool_overflow variable, the latter which is an unknown option we assume now, it probably should be max_overflow?

With a very low pool size it seems to work, but we still don't understand why the prepared statement count goes up. Also we would prefer to have a pool_size of 10, since that is what we use on all our projects. But then again, none of the other projects with myxql perform that many queries. We have only one other user case that does that, and it worked fine, but it using mariaex. And reverting our current app to mariaex does not seem feasible now, too many deps on myxql and/or Ecto 3.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions