<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="222089" data-post-id="222089">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tj0" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tj0
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I looked at this months ago and thought to myself finally I’m totally going to use this in production one day since I have a ton of read-only data that never gets updated.</p>
<p>And the day is coming pretty soon. I just ported over typeahead locations from postgres and FTS5 is working pretty well and ecto is doing its thing. Before flipping the switch, I wanted to understand the limitations from a system level.</p>
<p>I started reading <a href="https://sqlite.org/c3ref/open.html" rel="noopener nofollow ugc">sqlite open docs</a> and <a href="https://www.sqlite.org/lockingv3.html" rel="noopener nofollow ugc">locking</a> and <a href="https://sqlite.org/malloc.html" rel="noopener nofollow ugc">memory</a>, but everyone in the thread seems pretty knowledgeable, so I thought to ask what is actually happening under the covers?</p>
<p>What I’ve deduced so far is the following:</p>
<ol>
<li>
<p>The sqlite adapter does an fopen to the database. This will return a new fd. This fd will be used by the each of the processes of the Repo pool of which there are 5 by default. I’m just not sure if it’s a single fd for the BEAM  or an fd for each process of the pool.</p>
</li>
<li>
<p>It looks like locking is set to “NORMAL” which means that the database is unlocked after every transaction. This would mean that each of the 5 repo processes can only do queries sequentially on that single filehandle. Of course, if there are 5 different fds, I still don’t quite understand the locking. It looks like a SHARED lock is an internal tracking state for sqlite, but it seems like opening a read-only copy seems to be possible with <a href="https://sqlite.org/c3ref/open.html" rel="noopener nofollow ugc">sqlite_open_v2</a> and <a href="https://sqlite.org/sharedcache.html" rel="noopener nofollow ugc">sqlite shared-cache</a> which they have subsequently said <a href="https://sqlite.org/c3ref/enable_shared_cache.html" rel="noopener nofollow ugc">not to use</a> .</p>
</li>
<li>
<p>BEAM should know nothing about the memory management of this since it only knows about the fd and sqlite manages its memory dynamically. But since we’ve opened up the FD inside the BEAM process, the memory should be allocated to the BEAM process? I just couldn’t find any major changes in the resident memory or via observer, but it could just be my database isn’t big enough. Linux should handle all the page allocations and what not, so nothing fancy is needed.</p>
</li>
<li>
<p>Reading further, it does appear that there is a new FD for each Repo pool process? At least that’s what it appears to be happening from <a href="https://hexdocs.pm/ecto_sql/Ecto.Adapters.SQL.Connection.html" class="inline-onebox" rel="noopener nofollow ugc">Ecto.Adapters.SQL.Connection — Ecto SQL v3.14.0</a>? So just a bit confused as to how the memory is managed here. Does each sqlite open put it’s memory on the data / stack of the elixir pid?</p>
</li>
</ol>
<p>Considering that I’m only looking to use this for readonly data, it seems to be pragmatic to open up a new fd per elixir pid and open with a shared lock across all of them.</p>
<p>So instead of writing all of that, I should have probably just asked:</p>
<ol>
<li>Does each elixir pid have it’s own fd? If so, how is memory and locking managed for a read-only workload? Are we dynamically loading the sqlite index into the memory of each pid?</li>
<li>If each elixir pid doesn’t have it’s own fd, how would we stretch the read-only performance?</li>
</ol>
<p>Help?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="222089" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/ecto-sqlite3-an-ecto3-sqlite3-adapter/38391/32">Post #31</a>
	                </div>
	            </div>
              <div id="likers-container-222089" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="222089"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #31"></div>
  </section>
</div>
    <div class="postbit" id="222110" data-post-id="222110">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="kevinlang" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  kevinlang
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<ol>
<li>Number of handles = number of pool connections, more or less. Locking is handled by the underlying SQLite3 C library, a level “below” <code>exqlite</code> and <code>ecto_sqlite3</code>. The amount of SQLite3 data the SQLite3 library keeps in memory is determined by the <a href="https://www.sqlite.org/pragma.html#pragma_cache_size" rel="noopener nofollow ugc">cache_size</a> setting, which we set at <code>-2000</code> or 2MiB. In addition to that there is of course all of the overhead of the <code>exqlite</code> library - the NIF code, the Elixir code, the Elixir data objects and so on.</li>
<li>Read up on WAL, which is enabled by default for <code>ecto_sqlite3</code>. <a href="https://sqlite.org/wal.html" class="inline-onebox" rel="noopener nofollow ugc">Write-Ahead Logging</a> . TLDR: reads happen concurrently, there should be no scaling concern.</li>
</ol>
<p>We have some benchmark files in the <code>ecto_sqlite3</code> repo you can play around with to get an idea for performance. In my experience, SQLite3 out-performs Postgres by a noticeable margin, even on insert-heavy workloads (IFF using WAL journal mode), the benchmarks also reflect that. Of course, the tradeoff is that it has <em>way</em> less features, and is limited to a single node. Not having any of the client/server overhead goes a long way for performance.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="222110" data-batch-url="/posts/batch_likers">
                        4
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/ecto-sqlite3-an-ecto3-sqlite3-adapter/38391/33">Post #32</a>
	                </div>
	            </div>
              <div id="likers-container-222110" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="222110"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #32"></div>
  </section>
</div>
    <div class="postbit" id="222112" data-post-id="222112">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tj0" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tj0
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="kevinlang" data-post="33" data-topic="38391">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/k/d9b06d/48.png" class="avatar"> kevinlang:</div>
<blockquote>
<p>Number of handles = number of pool connections, more or less. Locking is handled by the underlying SQLite3 C library, a level “below” <code>exqlite</code> and <code>ecto_sqlite3</code>.</p>
</blockquote>
</aside>
<p>Cool, this makes sense. At that point, sqlite is probably using a mutex/semaphore underneath, so we shouldn’t need to set anything manually. It is competing with fopen after all.</p>
<aside class="quote no-group" data-username="kevinlang" data-post="33" data-topic="38391">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/k/d9b06d/48.png" class="avatar"> kevinlang:</div>
<blockquote>
<p>The amount of SQLite3 data the SQLite3 library keeps in memory is determined by the <a href="https://www.sqlite.org/pragma.html#pragma_cache_size" rel="noopener nofollow ugc">cache_size</a> setting, which we set at <code>-2000</code> or 2MiB.</p>
</blockquote>
</aside>
<p>Not quite understanding this yet, but perhaps this has more to do with sqlite. I’m assuming it isn’t trying to do a mmap of the indexes. When it reads from disk, those pages should be cached by linux, so that memory should show up in buffers/caches and not in beam.</p>
<p>Then there’s the pragma cache limit which appears to be separate. I suppose the nif is doing it’s own malloc as it would need to have some memory when doing large queries.  I was playing around with observer last night and there were hardly any memory changes that I found. <em>Does using malloc mean that the memory doesn’t show up in observer/beam or did I just miss it?</em></p>
<p>If my understanding is correct, the memory usage should be:</p>
<ul>
<li>per pool connection - up to 2MB sqlite cache + general elixir overhead + any malloc overhead for being able to execute the queries</li>
<li>linux virtual memory - caching/flushing the pages of the database file in memory which is shared across all fd’s</li>
</ul>
<p><em>Does that sound right?</em></p>
<aside class="quote no-group" data-username="kevinlang" data-post="33" data-topic="38391">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/k/d9b06d/48.png" class="avatar"> kevinlang:</div>
<blockquote>
<p>We have some benchmark files in the <code>ecto_sqlite3</code> repo you can play around with to get an idea for performance.</p>
</blockquote>
</aside>
<p>Interesting results. I didn’t think the networking overhead would cost that much for the inserts. Before doing the port of my data, I quickly checked the performance of the entire phoenix call between postgres and sqlite and it was between 50-100ms for both of them. This may seem unusually high, but I’m testing on a 10 year old laptop in dev mode. In prod, we’re looking at under 10ms for postgres.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="222112" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/ecto-sqlite3-an-ecto3-sqlite3-adapter/38391/34">Post #33</a>
	                </div>
	            </div>
              <div id="likers-container-222112" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="222112"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #33"></div>
  </section>
</div>
    <div class="postbit" id="222113" data-post-id="222113">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="kevinlang" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  kevinlang
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="tj0" data-post="34" data-topic="38391">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/t/48db29/48.png" class="avatar"> tj0:</div>
<blockquote>
<p>Does using malloc mean that the memory doesn’t show up in observer/beam or did I just miss it?</p>
</blockquote>
</aside>
<p>Not entirely sure, tbh. I haven’t had much exposure to observer before. NIFs are definitely quite different than other sort of processes in BEAM, especially dirty NIFs, a relatively new type, that <code>exqlite</code> uses. They run on separate their own schedulers and so on.</p>
<p>Are you using <code>exqlite</code> directly or using <code>ecto_sqlite3</code> in your experiments? In either case you can modify most of the underlying PRAGMAs we set at the beginning of the connection. For example, you could try increasing the <code>cache_size</code> to a much larger value and see if it makes a difference in your metrics.</p>
<aside class="quote no-group" data-username="tj0" data-post="34" data-topic="38391">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/t/48db29/48.png" class="avatar"> tj0:</div>
<blockquote>
<p>Not quite understanding this yet, but perhaps this has more to do with sqlite. I’m assuming it isn’t trying to do a mmap of the indexes. When it reads from disk, those pages should be cached by linux, so that memory should show up in buffers/caches and not in beam.</p>
</blockquote>
</aside>
<p>It may also be useful to create a simple C program using SQLite3 API to verify your assumptions before digging deeper into the NIF specifics, which as you see makes things a whole bunch more complicated! Unfortunately the amount of collective knowledge and understanding around NIFs, especially dirty NIFs, is pretty lacking. The <a href="https://erlang.org/doc/man/erl_nif.html" rel="nofollow">NIF docs</a> may also give you more insight into what you are looking for.</p>
<p>Good luck!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="222113" data-batch-url="/posts/batch_likers">
                        3
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/ecto-sqlite3-an-ecto3-sqlite3-adapter/38391/35">Post #34</a>
	                </div>
	            </div>
              <div id="likers-container-222113" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="222113"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #34"></div>
  </section>
</div>
    <div class="postbit" id="222134" data-post-id="222134">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tj0" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tj0
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="kevinlang" data-post="35" data-topic="38391">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/k/d9b06d/48.png" class="avatar"> kevinlang:</div>
<blockquote>
<p>Not entirely sure, tbh. I haven’t had much exposure to observer before. NIFs are definitely quite different than other sort of processes in BEAM, especially dirty NIFs, a relatively new type, that <code>exqlite</code> uses. They run on separate their own schedulers and so on.</p>
</blockquote>
</aside>
<p>Dirty nifs are pretty fantastic. Been using them in production since 2019 I think. Haven’t run into an issue with them yet.</p>
<aside class="quote no-group" data-username="kevinlang" data-post="35" data-topic="38391">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/k/d9b06d/48.png" class="avatar"> kevinlang:</div>
<blockquote>
<p>Are you using <code>exqlite</code> directly or using <code>ecto_sqlite3</code> in your experiments? In either case you can modify most of the underlying PRAGMAs we set at the beginning of the connection. For example, you could try increasing the <code>cache_size</code> to a much larger value and see if it makes a difference in your metrics.</p>
</blockquote>
</aside>
<p>I don’t think that will be necessary anymore as I found some changing metrics. I can’t tell if it’s a memory leak, but it does seem rather unexpected. The resident memory on beam keeps increasing. It’s not detected on observer, so must be the sqlite malloc or cache. It appears to be increasing by a few MB on every query. Using ecto_sqlite3, erlang 24.02 on dev, and erlang 23.2.3 on the test server. exqlite is at 0.6.1 and ecto_sqlite3 is on master.</p>
<p>I pushed this to a user-test server earlier this evening. Since the test server only has 512MB ram, it OOM’d pretty quickly. I’ve tried this on my laptop, but I cannot get it to increase past 850MB of resident memory. So it is totally behaving like it is a memory leak to a point, but then after a certain point, it is completely stable. I didn’t see anything unusual in the nif code, but I didn’t look too closely.</p>
<p>I’ll have to do some more testing in the next few days since I don’t really understand how any of this is possible. The only thing that might be out of the ordinary is that I’m using FTS5, but that’s a standard built-in extension. I suppose it could be the pragma cache…anyway, will need to do some more digging in the next few days.</p>
<p>Has anyone else run into issues with the beam resident memory increasing on every query to sqlite that stabilizes after some time? Any strangeness at all?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="222134" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/ecto-sqlite3-an-ecto3-sqlite3-adapter/38391/36">Post #35</a>
	                </div>
	            </div>
              <div id="likers-container-222134" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="222134"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #35"></div>
  </section>
</div>
    <div class="postbit" id="222141" data-post-id="222141">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="kevinlang" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  kevinlang
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="tj0" data-post="36" data-topic="38391">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/t/48db29/48.png" class="avatar"> tj0:</div>
<blockquote>
<p>Has anyone else run into issues with the beam resident memory increasing on every query to sqlite that stabilizes after some time? Any strangeness at all?</p>
</blockquote>
</aside>
<p>Check out <a href="https://github.com/elixir-sqlite/exqlite/issues/153" rel="noopener nofollow ugc">this issue</a>.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="222141" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/ecto-sqlite3-an-ecto3-sqlite3-adapter/38391/37">Post #36</a>
	                </div>
	            </div>
              <div id="likers-container-222141" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="222141"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #36"></div>
  </section>
</div>
    <div class="postbit" id="222244" data-post-id="222244">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="tj0" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  tj0
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>OK, so the memory usage on the 512 MB system was expected behaviour due to the cache setting.  However, from my testing, there is a potential memory leak which I haven’t tracked down how to fix quite yet. But I’m going to put it into production anyway which a reduced cache_size as a mitigation.</p>
<h3><a name="p-222244-performance-1" class="anchor" href="#p-222244-performance-1" aria-label="Heading link" rel="nofollow"></a>Performance</h3>
<p>Tested against:</p>
<ul>
<li>sqlite</li>
<li><a href="https://hex.pm/packages/raw_sqlite3" class="inline-onebox" rel="nofollow">raw_sqlite3 | Hex</a></li>
<li><a href="https://hex.pm/packages/exqlite" class="inline-onebox" rel="nofollow">exqlite | Hex</a></li>
<li><a href="https://hex.pm/packages/ecto_sqlite3" class="inline-onebox" rel="nofollow">ecto_sqlite3 | Hex</a></li>
</ul>
<p>Generated random queries on full-text search that I knew would hit based on bi-gram + wildcard “ba*”, etc.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">
CPU Information: Intel(R) Core(TM) i5-3320M CPU @ 2.60GHz
Number of Available Cores: 4
Available memory: 7.48 GB
Elixir 1.11.4
Erlang 23.2.1

Name                   ips        average  deviation         median         99th %
exsqlite             86.12       11.61 ms    ±85.74%        9.51 ms       49.56 ms
raw_sqlite           83.64       11.96 ms    ±89.03%        9.28 ms       51.83 ms
ecto_sqlite3         43.93       22.76 ms    ±74.73%       19.87 ms       72.09 ms

Comparison: 
exsqlite             86.12
raw_sqlite           83.64 - 1.03x slower +0.34 ms
ecto_sqlite3         43.93 - 1.96x slower +11.15 ms

</code></pre>
<p>The difference between raw_sqlite and exsqlite is due to the using a prepared statement for exqlite instead of a raw statement.</p>
<h3><a name="p-222244-memory-2" class="anchor" href="#p-222244-memory-2" aria-label="Heading link" rel="nofollow"></a>Memory</h3>
<p>When tested from IEX, all the sqlite nifs used heap memory space (eheap_alloc). The size of the heap memory used is based on the size of the result of the query and was allocated against iex.</p>
<p>When tested with benchee, for ecto, I couldn’t find the memory being allocated in observer at all. eheap_alloc stayed 13MB the entire time, but resident memory shot up to 500-600MB. Running :erlang.garbage_collect did nothing. The resident memory remained consistent over multiple benchee remained consistent at  around 590MB.</p>
<p>But I couldn’t find it. To test further, I killed all Repo for sqlite and the processes “Elixir.DBConnection.Connection:init/1” were all re-spawned. Ran garbage collection again, and nothing was collected. Re-ran benchee and the resident memory doubled to 1GB.</p>
<h3><a name="p-222244-conclusion-3" class="anchor" href="#p-222244-conclusion-3" aria-label="Heading link" rel="nofollow"></a>Conclusion</h3>
<p>The behaviour I was seeing about consistent memory growth on low-memory systems was not a bug - just 10 workers each consuming 64 MB of cache each. I suggest you put that into the documentation, otherwise people on their raspberry pi’s might have a bad time.</p>
<p>However, memory may leak in other ways (unless I went bug-eyed and lost my mind during the testing process). The exqlite process sets the default  cache_size: -64000. In some cases, this memory does not seem to be allocated to any erlang process and then can’t be garbage collected if the process is killed. Shrink_memory, if we could call it, might be able to reclaim it. Or potentially some other sqlite setting where we can ensure that the sqlite cache is on the erlang process heap and not somehow allocated somewhere else. This can be mitigated by reducing the cache_size also.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="222244" data-batch-url="/posts/batch_likers">
                        4
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/ecto-sqlite3-an-ecto3-sqlite3-adapter/38391/38">Post #37</a>
	                </div>
	            </div>
              <div id="likers-container-222244" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="222244"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #37"></div>
  </section>
</div>
    <div class="postbit" id="222246" data-post-id="222246">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="kevinlang" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  kevinlang
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Wow, thanks for looking into this so thoroughly!</p>
<p>Is this a fair summary?</p>
<ol>
<li>~600 MB memory is expected, because of overly large default cache size</li>
<li>However, it did balloon above that when killing all Repo processes. Seems like on kill we do not cleanly close the DB connection, which would explain it.</li>
<li>Our NIF memory usage does not show in observer. Imo this <em>may</em> be due to the fact that SQLite3 does its own memory management and does not use the NIF alloc functions (see below).</li>
</ol>
<aside class="quote no-group" data-username="tj0" data-post="38" data-topic="38391">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/t/48db29/48.png" class="avatar"> tj0:</div>
<blockquote>
<p>just 10 workers each consuming 64 MB of cache each. I suggest you put that into the documentation, otherwise people on their raspberry pi’s might have a bad time.</p>
</blockquote>
</aside>
<p>This is done in the <code>ecto_sqlite3</code> library defaults. We have that opinionated default in the documentation, but it was decided somewhat arbitrarily. See <a href="https://github.com/elixir-sqlite/ecto_sqlite3/issues/11" rel="noopener nofollow ugc">this issue</a>. I created a new issue to consider lowering it, it definitely seems like 2MB is a fine enough default. I created an issue for that, <a href="https://github.com/elixir-sqlite/ecto_sqlite3/issues/43" rel="noopener nofollow ugc">here</a>.</p>
<p>If you could share snippets of your benchmarking code or just the more specific process or calls you do in the IEX or whatever (for those with no experience in these things, like myself), that would be greatly appreciated! I could use that info to try debugging further.</p>
<aside class="quote no-group" data-username="tj0" data-post="38" data-topic="38391">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/t/48db29/48.png" class="avatar"> tj0:</div>
<blockquote>
<p>Or potentially some other sqlite setting where we can ensure that the sqlite cache is on the erlang process heap and not somehow allocated somewhere else.</p>
</blockquote>
</aside>
<p>Yes, this could perhaps be done. As mentioned above, I think the fact SQLite3 uses its own malloc is why this is. It <em>may</em> be possible to provide the erlang memory management functions at DB init time, which could incidentally fix the memory leak issue we saw, perhaps.</p>
<p>See this <a href="https://www.sqlite.org/c3ref/mem_methods.html" rel="noopener nofollow ugc">SQLite3 documentation</a>. Not sure if the NIF memory functions provided can fit this interface entirely, though.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="222246" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/ecto-sqlite3-an-ecto3-sqlite3-adapter/38391/39">Post #38</a>
	                </div>
	            </div>
              <div id="likers-container-222246" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="222246"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #38"></div>
  </section>
</div>
    <div class="postbit" id="222247" data-post-id="222247">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="warmwaffles" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/warmwaffles/120/22057_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  warmwaffles
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="tj0" data-post="38" data-topic="38391">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/t/48db29/48.png" class="avatar"> tj0:</div>
<blockquote>
<p>However, memory may leak in other ways (unless I went bug-eyed and lost my mind during the testing process). The exqlite process sets the default cache_size: -64000. In some cases, this memory does not seem to be allocated to any erlang process and then can’t be garbage collected if the process is killed.</p>
</blockquote>
</aside>
<p>Before we change anything around that default cache size, can you do a similar test and lower the value to something like <code>-2000</code> and check out the results? I originally picked <code>-64000</code> because that’s what I saw suggested for applications that were going to utilize a heavy read load on an sqlite database. But now that I think about it, yea it’s a bad default for Pis or any sort of embedded device that has very limited memory.</p>
<p>As for a memory leak, someone did report one but I am unable to reproduce it. Right now with how the NIF is structure, the prepared statements are deconstructed when the handle’s ref count goes to zero.</p>
<p>Could you provide your benchmark setup? I’d love to put that in a repository to be able to run.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="222247" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/ecto-sqlite3-an-ecto3-sqlite3-adapter/38391/40">Post #39</a>
	                </div>
	            </div>
              <div id="likers-container-222247" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="222247"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #39"></div>
  </section>
</div>
    <div class="postbit" id="222249" data-post-id="222249">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="kevinlang" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  kevinlang
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="kevinlang" data-post="39" data-topic="38391">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/k/d9b06d/48.png" class="avatar"> kevinlang:</div>
<blockquote>
<p>However, it did balloon above that when killing all Repo processes. Seems like on kill we do not cleanly close the DB connection, which would explain it.</p>
</blockquote>
</aside>
<p>Actually, we do have a destructor registered, which should be called…</p>
<p>I wonder if this may be due to the destructor being called in a delayed manner. I see some messages on mailing lists about something like that. May be worth adding some <code>enif_fprintf</code> in the relevant destructor calls to verify</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="222249" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/ecto-sqlite3-an-ecto3-sqlite3-adapter/38391/41">Post #40</a>
	                </div>
	            </div>
              <div id="likers-container-222249" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="222249"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #40"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <a class="load-more-button" data-turbo-stream="true" href="/topics/38391/load_more?page=5">Load more posts (12 remaining)</a>
</div></template></turbo-stream>