Coder Social home page Coder Social logo

litestack's Introduction

litestack

Gem Version RubyGems Downloads

All your data infrastructure, in a gem!

Litestack is a Ruby gem that provides both Ruby and Ruby on Rails applications an all-in-one solution for web application data infrastructure. It exploits the power and embeddedness of SQLite to deliver a full-fledged SQL database, a fast cache , a robust job queue, a reliable message broker, a full text search engine and a metrics platform all in a single package.

Compared to conventional approaches that require separate servers and databases, Litestack offers superior performance, efficiency, ease of use, and cost savings. Its embedded database and cache reduce memory and CPU usage, while its simple interface streamlines the development process. Overall, Litestack sets a new standard for web application development and is an excellent choice for those who demand speed, efficiency, and simplicity.

You can read more about why litestack can be a good choice for your next web application here, you might also be interested in litestack benchmarks.

With litestack you only need to add a single gem to your app which would replace a host of other gems and services, for example, a typical Rails app using litestack will no longer need the following services:

  • Database Server (e.g. PostgreSQL, MySQL)
  • Cache Server (e.g. Redis, Memcached)
  • Job Processor (e.g. Sidekiq, Goodjob)
  • Pubsub Server (e.g. Redis, PostgreSQL)
  • Fulltext Search Server (e.g. Elasticsearch, Meilisearch)

To make it even more efficient, litestack will detect the presence of Fiber based IO frameworks like Async (e.g. when you use the Falcon web server) or Polyphony. It will then switch its background workers for caches and queues to fibers (using the semantics of the existing framework). This is done transparently and will generally lead to lower CPU and memory utilization. litestack

Installation

Add the litestack gem line to your application's Gemfile:

$ bundle add litestack

To configure a Rails application to run the full litestack, run:

$ rails generate litestack:install

Usage

litestack currently offers six main components

  • litedb
  • litecache
  • litejob
  • litecable
  • litesearch
  • litemetric

litedb

litedb is a wrapper around SQLite3, offering a better default configuration that is tuned for concurrency and performance. Out of the box, litedb works seamlessly between multiple processes without database locking errors. litedb can be used in multiple ways, including:

Direct litedb usage

litedb can be used exactly as the SQLite3 gem, since litedb inherits from SQLite3

require 'litestack'
db = Litedb.new(path_to_db)
db.execute("create table users(id integer primary key, name text)")
db.execute("insert into users(name) values (?)", "Hamada")
db.query("select count(*) from users") # => [[1]]

ActiveRecord

litedb provides tight Rails/ActiveRecord integration and can be configured as follows

In database.yml

adapter: litedb
# normal sqlite3 configuration follows

Sequel

litedb offers integration with the Sequel database toolkit and can be configured as follows

DB = Sequel.connect("litedb://path_to_db_file")

litecache

litecache is a high speed, low overhead caching library that uses SQLite as its backend. litecache can be accessed from multiple processes on the same machine seamlessly. It also has features like key expiry, LRU based eviction and increment/decrement of integer values.

Direct litecache usage

require 'litestack'
cache = Litecache.new(path: "path_to_file")
cache.set("key", "value")
cache.get("key") #=> "value"

ActiveResource::Cache

In your desired environment file (e.g. production.rb)

config.cache_store = :litecache, {path: './path/to/your/cache/file'}

This provides a transparent integration that uses the Rails caching interface

litecache spawns a background thread for cleanup purposes. In case it detects that the current environment has Fiber::Scheduler or Polyphony loaded it will spawn a fiber instead, saving on both memory and CPU cycles.

litejob

More info about Litejob can be found in the litejob guide

litejob is a fast and very efficient job queue processor for Ruby applications. It builds on top of SQLite as well, which provides transactional guarantees, persistence and exceptional performance.

Direct litejob usage

require 'litestack'
# define your job class
class MyJob
  include ::Litejob
      
  queue = :default
      
  # must implement perform, with any number of params
  def perform(params)
    # do stuff
  end
end
    
#schedule a job asynchronusly
MyJob.perform_async(params)
    
#schedule a job at a certain time
MyJob.perform_at(time, params)
    
#schedule a job after a certain delay
MyJob.perform_after(delay, params)

ActiveJob

In your desired environment file (e.g. production.rb)

config.active_job.queue_adapter = :litejob

Configuration file

You can add more configuration in litejob.yml (or config/litejob.yml if you are integrating with Rails)

queues:
    - [default, 1]
    - [urgent, 5]
    - [critical, 10, "spawn"]

The queues need to include a name and a priority (a number between 1 and 10) and can also optionally add the token "spawn", which means every job will run it its own concurrency context (thread or fiber)

litecable

ActionCable

This is a drop in replacement adapter for actioncable that replaces async and other production adapters (e.g. PostgreSQL, Redis). This adapter is currently only tested in local (inline) mode.

Getting up and running with litecable requires configuring your cable.yaml file under the config/ directory

cable.yaml

development:
  adapter: litecable

test:
  adapter: test

staging:
  adapter: litecable

production:
  adapter: litecable

litesearch

Litesearch

Litesearch adds full text search capabilities to Litedb, you can use it in standalone mode as follows:

require 'litestack/litedb'
db = Litedb.new(":memory:")
# create the index
idx = db.search_index('index_name') do |schema|
    schema.fields [:sender, :receiver, :body]
    schema.field :subject, weight: 10
    schema.tokenizer :trigram
end
# add documents
idx.add({sender: 'Kamal', receiver: 'Laila', subject: 'Are the girls awake?', body: 'I got them the new phones they asked for, are they awake?'})
# search the index, all fields
idx.search('kamal')
# search the index, specific field, partial workd (trigram)
idx.search('subject: awa') 

Litesearch integrates tightly with ActiveRecord and Sequel, here are integration examples

ActiveRecord

class Author < ActiveRecord::Base
    has_many :books
end

class Book < ActiveRecord::Base
    belongs_to :author

    include Litesearch::Model

    litesearch do |schema|
        schema.fields [:title, :description]
        schema.field :author, target: 'authors.name'
        schema.tokenizer :porter
    end
end
# insert records
Author.create(name: 'Adam A. Writer') 
Book.create(title: 'The biggest stunt', author_id: 1, description: 'a description') 
# search the index, the search method integrates with AR's query interface
Book.search('author: writer').limit(1).all

Sequel

class Author < Sequel::Model
    one_to_many :books
end

class Book < Sequel::Model
    many_to_one :author

    include Litesearch::Model
    litesearch do |schema|
        schema.fields [:title, :description]
        schema.field :author, target: 'authors.name'
        schema.tokenizer :porter
    end
end
# insert records
Author.create(name: 'Adam A. Writer') 
Book.create(title: 'The biggest stunt', author_id: 1, description: 'a description') 
# search the index, the search method integrates with Sequel's query interface
Book.search('author: writer').limit(1).all

litemetric

Litemetric

Litestack comes with a module that can collect useful metrics for its different components, in each component, you need to add the following to the respective .yml file (database.yml in case of Litedb)

    metrics: true # default is false

If you have the metrics enabled, it will start collecting data from the various modules and will store them in a database file called metric.db located in the Litesupport.root folder

Litemetric has an API that would enable collecting arbitrary metrics for non-litestack classes. The metrics will be in the database but currently the Liteboard is only able to show correct data for Litestack modules, displaying arbitrary metrics for other components will be included later.

Liteboard

Liteboard is a simple web server that provides a web interface for the collected metrics, it should be available globally, for usage instructions type

    liteboard -h

It allows you to point to a specific metrics database file or a config file and then it will display the data in that metrics database

Example metrics views:

Litedb

litedb

  • Database size, number of tables & indexes
  • Number of read/write queries
  • Read/Write query ratio over time
  • Read/Write query time over time
  • Slowest queries
  • Most expensive queries (total run time = frequency * cost)

Litecache

litecache

  • Cache size, % of size limit
  • Number of entries
  • Reads/Writes over time
  • Read hits/misses over time
  • Most written entries
  • Most read entries

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/oldmoe/litestack.

License

The gem is available as open source under the terms of the MIT License.

litestack's People

Contributors

adrienpoly avatar alyhkafoury avatar bbonamin avatar bopm avatar bradgessler avatar enstyled avatar fangherk avatar fractaledmind avatar gabriel-curtino avatar jalessio avatar jmarsh24 avatar jodanjodan avatar julianrubisch avatar kaspth avatar lucianghinda avatar luizkowalski avatar marcoroth avatar mattbnz avatar nlsrchtr avatar oldmoe avatar olleolleolle avatar palkan avatar quatauta avatar rossta avatar rrevi avatar seabre avatar siyanda avatar sonicdes avatar westonganger avatar yenshirak avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

litestack's Issues

Using Litecable adapter for test environment?

Hello

While adding a system test to ensure the broadcast is working with Litestack, I wanted to use litecable adapter in the test environment in replacement of the test adapter.

My tests works ok with the test adapter but fail with the litecable adapter. I thought it would be a great benefit of Litestack to have the exact same configuration for development, production and test.

Could this be possible?

more details here:

adrienpoly/rubyvideo#50

Race condition between env and defaults when using LITESTACK_DATA_PATH with dotenv gem

If I add LITESTACK_DATA_PATH="./storage" to my .env it is picked up after the litestack initializes some classes so it uses the default path to create the databases, but later on, it tries to find databases files in LITESTACK_DATA_PATH (i think)

See this screenshot where I paused execution.

CleanShot 2024-01-29 at 23 37 12@2x

This leads to the app having sqlite3 files in 2 different parts:

CleanShot 2024-01-29 at 23 59 45@2x

Expected `config/litejob.yml` to be sent through ERb like everything else

I wrote the following config/litejob.yml in the context of a Rails 7.1 application:

path: <%= Litesupport.root("development").join("data.sqlite3") %>
queues:
  - [default, 1]

I was quite surprised to find the following files living in my application's root directoty 30 seconds later!

$ ls -l
total 408
-rw-r--r--   1 francois  staff   4096 Mar 24 22:21 <%= Litesupport.root("development").join("data.sqlite3") %>
-rw-r--r--   1 francois  staff  32768 Mar 24 22:23 <%= Litesupport.root("development").join("data.sqlite3") %>-shm
-rw-r--r--   1 francois  staff  86552 Mar 24 22:24 <%= Litesupport.root("development").join("data.sqlite3") %>-wal
$ bundle show
Gems included by the bundle:
  * litestack (0.4.3)
  * rails (7.1.3.2)
  * sqlite3 (1.7.3)

Incidentally, it isn't mentionned anywhere, but I presume that storing the queue in the same SQLite3 DB file as the application's data isn't a problem, provided I don't create a table called queue. I expect to be able to backup a database file, restore it elsewhere, and scheduled jobs should run whenever I start the main server.

Intention of Litesupport::Mutex?

Similar to #31, but I am trying to determine how Litesupport::Mutex and Litesupport.mutex are intended to relate, and why they both exist. There is a Litesupport::Mutex#synchronize and a Litesupport.synchronize method, which itself will call Litesupport::Mutex#synchronize in threaded environments.

My confusion is that Litesupport::Mutex doesn't fully implement the Mutex interface (as defined by the Thread::Mutex implementation) only synchronize, but we already have a globally available synchronize method in Litesupport. Given that, I'm a bit confused by the implementation of Litesupport.synchronize:

def self.synchronize(fiber_sync = false, &block)
if self.scheduler == :fiber or self.scheduler == :polyphony
yield # do nothing, just run the block as is
else
self.mutex.synchronize(&block)
end
end

and Litesupport::Mutex#synchronize:

def synchronize(&block)
if Litesupport.scheduler == :threaded || Litesupport.scheduler == :iodine
@mutex.synchronize{ block.call }
else
block.call
end
end

They appear to simply be mirror images of each other, so why do they both exist?

invalid insert_conflict support for sequel

Hi,

it seems that litedb sequel driver ignores/breaks insert_conflict functionality of sequel. Example:

require "sequel"
DB = Sequel.connect("litedb://test.db")

DB.create_table(:_systems) do
  primary_key :id
  String :name, unique: true, null: false
end

DB.create_table(:_nodes) do
  primary_key :id
  foreign_key :_system_id, :_systems, on_delete: :cascade
  String :hostname, null: false
  String :platform
  String :uri, null: false
end

class System < Sequel::Model(DB[:_systems])
  one_to_many :nodes, key: :_system_id
  one_to_many :tokens, key: :_system_id
end

class Node < Sequel::Model(DB[:_nodes])
  many_to_one :system, key: :_system_id

  plugin :validation_helpers
  plugin :insert_conflict

  def validate
    super
    validates_presence :hostname
    validates_presence :uri
  end
end

2.times do
  node = Node.new(hostname: "host1", uri: "ssh://1.1.1.1:22")
  node.id = 1
  node.insert_conflict(target: :id, update: { hostname: "host1" }).save
end
p Node.count

It will break with following log (notice, that INSERT statement if without ON CONFLICT clause):

I, [2024-02-06T22:25:07.018779 #30242]  INFO -- : (0.002177s) CREATE TABLE `_systems` (`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, `name` varchar(255) NOT NULL UNIQUE)
I, [2024-02-06T22:25:07.019082 #30242]  INFO -- : (0.000099s) CREATE TABLE `_nodes` (`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, `_system_id` integer REFERENCES `_systems` ON DELETE CASCADE, `hostname` varchar(255) NOT NULL, `platform` varchar(255), `uri` varchar(255) NOT NULL)
I, [2024-02-06T22:25:07.019315 #30242]  INFO -- : (0.000017s) BEGIN IMMEDIATE TRANSACTION
I, [2024-02-06T22:25:07.019449 #30242]  INFO -- : (0.000051s) PRAGMA table_info('_systems')
I, [2024-02-06T22:25:07.019514 #30242]  INFO -- : (0.000014s) COMMIT
I, [2024-02-06T22:25:07.019995 #30242]  INFO -- : (0.000015s) BEGIN IMMEDIATE TRANSACTION
I, [2024-02-06T22:25:07.020103 #30242]  INFO -- : (0.000049s) PRAGMA table_info('_nodes')
I, [2024-02-06T22:25:07.020164 #30242]  INFO -- : (0.000014s) COMMIT
I, [2024-02-06T22:25:07.021538 #30242]  INFO -- : (0.000015s) BEGIN IMMEDIATE TRANSACTION
I, [2024-02-06T22:25:07.021663 #30242]  INFO -- : (0.000025s) INSERT INTO `_nodes` (`hostname`, `uri`, `id`) VALUES ('host1', 'ssh://1.1.1.1:22', 1)
I, [2024-02-06T22:25:07.021781 #30242]  INFO -- : (0.000038s) SELECT * FROM `_nodes` WHERE `id` = 1
I, [2024-02-06T22:25:07.021868 #30242]  INFO -- : (0.000051s) COMMIT
I, [2024-02-06T22:25:07.022026 #30242]  INFO -- : (0.000017s) BEGIN IMMEDIATE TRANSACTION
E, [2024-02-06T22:25:07.022114 #30242] ERROR -- : SQLite3::ConstraintException: UNIQUE constraint failed: _nodes.id: INSERT INTO `_nodes` (`hostname`, `uri`, `id`) VALUES ('host1', 'ssh://1.1.1.1:22', 1)
I, [2024-02-06T22:25:07.022191 #30242]  INFO -- : (0.000014s) ROLLBACK
/usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sqlite3-1.7.1-x86_64-linux/lib/sqlite3/resultset.rb:100:in `step': SQLite3::ConstraintException: UNIQUE constraint failed: _nodes.id (Sequel::ConstraintViolation)
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sqlite3-1.7.1-x86_64-linux/lib/sqlite3/resultset.rb:100:in `next'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sqlite3-1.7.1-x86_64-linux/lib/sqlite3/resultset.rb:125:in `each'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/litestack-0.4.2/lib/litestack/litedb.rb:98:in `to_a'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/litestack-0.4.2/lib/litestack/litedb.rb:98:in `block in execute'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/litestack-0.4.2/lib/litestack/litedb.rb:72:in `prepare'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/litestack-0.4.2/lib/litestack/litedb.rb:88:in `execute'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/adapters/sqlite.rb:257:in `block (2 levels) in _execute'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/database/logging.rb:43:in `log_connection_yield'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/adapters/sqlite.rb:257:in `block in _execute'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/connection_pool/threaded.rb:88:in `hold'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/database/connecting.rb:293:in `synchronize'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/adapters/sqlite.rb:248:in `_execute'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/adapters/sqlite.rb:186:in `execute_insert'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/dataset/actions.rb:1205:in `execute_insert'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/dataset/actions.rb:423:in `insert'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1744:in `_insert_raw'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1726:in `_insert'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1794:in `block (2 levels) in _save'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1047:in `around_create'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1791:in `block in _save'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1047:in `around_save'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1786:in `_save'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1500:in `block (2 levels) in save'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/database/transactions.rb:264:in `_transaction'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/database/transactions.rb:239:in `block in transaction'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/connection_pool/threaded.rb:92:in `hold'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/database/connecting.rb:293:in `synchronize'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/database/transactions.rb:197:in `transaction'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1954:in `checked_transaction'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1500:in `block in save'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1940:in `checked_save_failure'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1500:in `save'
        from t.rb:44:in `block in <main>'
        from <internal:numeric>:237:in `times'
        from t.rb:41:in `<main>'
/usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sqlite3-1.7.1-x86_64-linux/lib/sqlite3/resultset.rb:100:in `step': UNIQUE constraint failed: _nodes.id (SQLite3::ConstraintException)
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sqlite3-1.7.1-x86_64-linux/lib/sqlite3/resultset.rb:100:in `next'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sqlite3-1.7.1-x86_64-linux/lib/sqlite3/resultset.rb:125:in `each'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/litestack-0.4.2/lib/litestack/litedb.rb:98:in `to_a'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/litestack-0.4.2/lib/litestack/litedb.rb:98:in `block in execute'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/litestack-0.4.2/lib/litestack/litedb.rb:72:in `prepare'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/litestack-0.4.2/lib/litestack/litedb.rb:88:in `execute'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/adapters/sqlite.rb:257:in `block (2 levels) in _execute'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/database/logging.rb:43:in `log_connection_yield'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/adapters/sqlite.rb:257:in `block in _execute'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/connection_pool/threaded.rb:88:in `hold'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/database/connecting.rb:293:in `synchronize'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/adapters/sqlite.rb:248:in `_execute'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/adapters/sqlite.rb:186:in `execute_insert'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/dataset/actions.rb:1205:in `execute_insert'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/dataset/actions.rb:423:in `insert'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1744:in `_insert_raw'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1726:in `_insert'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1794:in `block (2 levels) in _save'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1047:in `around_create'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1791:in `block in _save'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1047:in `around_save'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1786:in `_save'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1500:in `block (2 levels) in save'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/database/transactions.rb:264:in `_transaction'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/database/transactions.rb:239:in `block in transaction'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/connection_pool/threaded.rb:92:in `hold'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/database/connecting.rb:293:in `synchronize'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/database/transactions.rb:197:in `transaction'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1954:in `checked_transaction'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1500:in `block in save'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1940:in `checked_save_failure'
        from /usr/lib/fullstaq-ruby/versions/3.3.0-jemalloc/lib/ruby/gems/3.3.0/gems/sequel-5.77.0/lib/sequel/model/base.rb:1500:in `save'
        from t.rb:44:in `block in <main>'
        from <internal:numeric>:237:in `times'
        from t.rb:41:in `<main>'

When you switch litedb:// to sqlite:// you get following output:

I, [2024-02-06T22:26:32.192244 #30512]  INFO -- : (0.004684s) CREATE TABLE `_systems` (`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, `name` varchar(255) NOT NULL UNIQUE)
I, [2024-02-06T22:26:32.196462 #30512]  INFO -- : (0.004014s) CREATE TABLE `_nodes` (`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, `_system_id` integer REFERENCES `_systems` ON DELETE CASCADE, `hostname` varchar(255) NOT NULL, `platform` varchar(255), `uri` varchar(255) NOT NULL)
I, [2024-02-06T22:26:32.196744 #30512]  INFO -- : (0.000038s) SELECT sqlite_version()
I, [2024-02-06T22:26:32.196883 #30512]  INFO -- : (0.000051s) PRAGMA table_xinfo('_systems')
I, [2024-02-06T22:26:32.197471 #30512]  INFO -- : (0.000078s) PRAGMA table_xinfo('_nodes')
I, [2024-02-06T22:26:32.198855 #30512]  INFO -- : (0.000012s) BEGIN
I, [2024-02-06T22:26:32.199113 #30512]  INFO -- : (0.000140s) INSERT INTO `_nodes` (`hostname`, `uri`, `id`) VALUES ('host1', 'ssh://1.1.1.1:22', 1) ON CONFLICT (`id`) DO UPDATE SET `hostname` = 'host1' RETURNING *
I, [2024-02-06T22:26:32.202219 #30512]  INFO -- : (0.003058s) COMMIT
I, [2024-02-06T22:26:32.202435 #30512]  INFO -- : (0.000014s) BEGIN
I, [2024-02-06T22:26:32.202606 #30512]  INFO -- : (0.000075s) INSERT INTO `_nodes` (`hostname`, `uri`, `id`) VALUES ('host1', 'ssh://1.1.1.1:22', 1) ON CONFLICT (`id`) DO UPDATE SET `hostname` = 'host1' RETURNING *
I, [2024-02-06T22:26:32.202668 #30512]  INFO -- : (0.000016s) COMMIT
I, [2024-02-06T22:26:32.202912 #30512]  INFO -- : (0.000038s) SELECT count(*) AS 'count' FROM `_nodes` LIMIT 1
1

No error, INSERT had ON CONFLICT, and there are only 1 Node in db.

I'm using ruby 3.3.0, litestack 0.4.2, sqlite3 1.7.1 with sqlite in version 3.45.0.

Rake tasks not supported by 'litedb' adapter

Trying to migrate an existing Rails app over to litestack. I'm getting this error when trying to run the db:test:prepare task.
The offending sub-task seems to be db:test:purge. Log is attached.

$ rake db:test:load --trace
** Invoke db:test:load (first_time)
** Invoke db:test:purge (first_time)
** Invoke db:load_config (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute db:load_config
** Invoke db:check_protected_environments (first_time)
** Invoke db:load_config 
** Execute db:check_protected_environments
** Execute db:test:purge
rake aborted!
ActiveRecord::Tasks::DatabaseNotSupported: Rake tasks not supported by 'litedb' adapter
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/activerecord-7.0.4.2/lib/active_record/tasks/database_tasks.rb:544:in `class_for_adapter'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/activerecord-7.0.4.2/lib/active_record/tasks/database_tasks.rb:534:in `database_adapter_for'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/activerecord-7.0.4.2/lib/active_record/tasks/database_tasks.rb:341:in `purge'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/activerecord-7.0.4.2/lib/active_record/railties/databases.rake:568:in `block (4 levels) in <main>'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/activerecord-7.0.4.2/lib/active_record/railties/databases.rake:567:in `each'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/activerecord-7.0.4.2/lib/active_record/railties/databases.rake:567:in `block (3 levels) in <main>'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/task.rb:281:in `block in execute'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/task.rb:281:in `each'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/task.rb:281:in `execute'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/task.rb:219:in `block in invoke_with_call_chain'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `synchronize'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `invoke_with_call_chain'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/task.rb:243:in `block in invoke_prerequisites'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/task.rb:241:in `each'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/task.rb:241:in `invoke_prerequisites'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/task.rb:218:in `block in invoke_with_call_chain'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `synchronize'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `invoke_with_call_chain'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/task.rb:188:in `invoke'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/application.rb:160:in `invoke_task'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/application.rb:116:in `block (2 levels) in top_level'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/application.rb:116:in `each'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/application.rb:116:in `block in top_level'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/application.rb:125:in `run_with_threads'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/application.rb:110:in `top_level'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/application.rb:83:in `block in run'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/application.rb:186:in `standard_exception_handling'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/lib/rake/application.rb:80:in `run'
/Users/se/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rake-13.0.6/exe/rake:27:in `<top (required)>'
/Users/se/.rbenv/versions/3.2.0/bin/rake:25:in `load'
/Users/se/.rbenv/versions/3.2.0/bin/rake:25:in `<main>'
Tasks: TOP => db:test:load => db:test:purge

generate_relation: Connection adapter not supported: litedb (Groupdate::Error)

I am trying to use the groupdate gem to group time series by dates

When I use the liltedb adapter add call a query like that

Ahoy::Visit.group_by_day(:started_at).count

I am getting this error

gems/3.2.0/gems/groupdate-6.2.1/lib/groupdate/magic.rb:208:in `generate_relation': Connection adapter not supported: litedb (Groupdate::Error)

The error is reported in groupedate code but I don't have the same error when I use the sqlite3 adapter

capistrano hangs after receiving "Litejob detected an exit, cleaning up"

I am trying to deploy my application with capistrano. It used to work fine before switching to litestack.

Now the deployment just hangs/stops with the last lines being

...snipped - yarn install success or bundle exec rake assets:clean...

DEBUG [0e815dbb] 	--- Litejob detected an exit, cleaning up
 --- Exiting with 0 jobs in flight

What do we need to do to fix this.

Litejob doesn't retry jobs on failure

I look forward to implementation of job retries.

/tmp/litestack-demo
โ–ถ pry
[1] pry(main)> require 'litestack'
=> true
[2] pry(main)> require 'sequel'
=> true
[3] pry(main)> class Job
[3] pry(main)*   include Litejob
[3] pry(main)*   def perform(args={})
[3] pry(main)*     puts "Starting job at #{Time.now}"
[3] pry(main)*     sleep 3
[3] pry(main)*     puts "Blowing up: #{Time.now}"
[3] pry(main)*     raise "Well shucks."
[3] pry(main)*   end
[3] pry(main)* end
=> :perform
[4] pry(main)> q = Sequel.connect('litedb://queue.db')
=> #<Sequel::Litedb::Database: "litedb://queue.db">
[5] pry(main)> q.tables
=> [:_ul_queue_]
[6] pry(main)> q[:_ul_queue_].all
=> []
[7] pry(main)> Job.perform_async
I, [2023-03-06T16:17:06.157153 #92521]  INFO -- : [litejob]:[ENQ] id: 1678141026-F04E622CB1A83B6E615.6 class: Job
=> "1678141026-F04E622CB1A83B6E615.6"
[8] pry(main)> I, [2023-03-06T16:17:06.450876 #92521]  INFO -- : [litejob]:[DEQ] id: 1678141026-F04E622CB1A83B6E615.6 class: Job
Starting job at 2023-03-06 16:17:06 -0600
[8] pry(main)> q[:_ul_queue_].all
=> []
[9] pry(main)> Blowing up: 2023-03-06 16:17:09 -0600
Well shucks.
Well shucks.
(pry):9:in `perform'
           # < snip >
[9] pry(main)>
[10] pry(main)>
[11] pry(main)> q[:_ul_queue_].all
=> []
[12] pry(main)>

`rails new` followed by installation and configuration fails to cache data?

I essentially ran the following:

rails new --database=sqlite3
bundle add litestack
# edit config/environments/development.rb
# replaced `:memory_store` with `config.cache_store = :litecache, {path: Litesupport.root(Rails.env).join("cache.sqlite3")}` (see below)

With the Rails server NOT running, I opened a console and ran the following two commands:

Rails.cache.write('foo', 'bar')
#=> true
Rails.cache.read('foo')
#=> nil

When I open the db/development/cache.sqlite3 file and query the data table, I can see the key added, but the value is always null.

# config/environments/development.rb
Rails.application.configure do
  # ...
  config.cache_store = :litecache, {path: Litesupport.root(Rails.env).join("cache.sqlite3")}
end
$ tree db
db
โ”œโ”€โ”€ seeds.rb
โ””โ”€โ”€ test

2 directories, 1 file
$ bin/rails console
Loading development environment (Rails 7.1.3.2)
irb(main):001> Rails.cache
=>
#<ActiveSupport::Cache::Litecache:0x00000001213088e8
 @cache=
  #<Litecache:0x000000012132bde8
   @bgthread=#<Thread:0x0000000121327dd8 /Users/francois/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/litestack-0.4.3/lib/litestack/litecache.rb:248 sleep>,
   @conn=#<Litesupport::Pool:0x0000000121308758 @block=#<Proc:0x000000012132bb18 /Users/francois/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0/gems/litestack-0.4.3/lib/litestack/litesupport.rb:235>, @count=1, @mutex=#<Litesupport::Mutex:0x000000012132baa0 @mutex=#<Thread::Mutex:0x000000012132ba78>>, @resources=#<Thread::Queue:0x000000012132baf0>>,
   @expires_in=2592000,
   @logger=#<Logger:0x0000000121307fd8 @default_formatter=#<Logger::Formatter:0x0000000121327e50 @datetime_format=nil>, @formatter=nil, @level=0, @level_override={}, @logdev=nil, @progname=nil>,
   @options={:path=>#<Pathname:./db/development/cache.sqlite3>, :config_path=>"./litecache.yml", :sync=>0, :expiry=>2592000, :size=>134217728, :mmap_size=>134217728, :min_size=>8388608, :return_full_record=>true, :sleep_interval=>30, :metrics=>false, :compress=>true, :compress_threshold=>1024, :logger=>nil},
   @running=true>,
 @coder=#<ActiveSupport::Cache::Coder:0x000000012132beb0 @compressor=Zlib, @legacy_serializer=false, @serializer=ActiveSupport::Cache::SerializerWithFallback::Marshal71WithFallback>,
 @coder_supports_compression=true,
 @options={:path=>#<Pathname:./db/development/cache.sqlite3>, :compress=>true, :compress_threshold=>1024, :return_full_record=>true}>

irb(main):002> Rails.cache.write('foo', 'value')
=> true

irb(main):003> Rails.cache.read('foo')
=> nil
$ tree db
db
โ”œโ”€โ”€ development
โ”‚ย ย  โ”œโ”€โ”€ cache.sqlite3
โ”‚ย ย  โ”œโ”€โ”€ queue.sqlite3
โ”‚ย ย  โ”œโ”€โ”€ queue.sqlite3-shm
โ”‚ย ย  โ””โ”€โ”€ queue.sqlite3-wal
โ”œโ”€โ”€ production
โ”œโ”€โ”€ seeds.rb
โ””โ”€โ”€ test

4 directories, 5 files

$ sqlite3 db/development/cache.sqlite3 'select * from data'
foo||1711335886|1711335886

From the Rails console, if I #instance_variable_get the Litecache instance, then I can set and get values correctly:

$ bin/rails c
cache = Rails.cache.instance_variable_get(:@cache)
#=>
#<Litecache:0x000000012b471a18

cache.set('bar', 'some value')
#=> true

cache.get('bar')
#=> "some value"

$ sqlite3 db/development/cache.sqlite3 'select * from data'
bar|some value|1713928160|1711336160
Gemfile, Gemfile.lock

Gemfile

source "https://rubygems.org"

ruby "3.3.0"

gem "bootsnap", require: false
gem "capybara", group: :test
gem "debug", platforms: %i[mri windows], groups: %i[development test]
gem "image_processing", "~> 1.2"
gem "importmap-rails"
gem "litestack"
gem "propshaft"
gem "puma", ">= 5.0"
gem "rails", "~> 7.1.3", ">= 7.1.3.2"
gem "selenium-webdriver", group: :test
gem "sqlite3", "~> 1.4"
gem "standardrb", group: :development
gem "stimulus-rails"
gem "tailwindcss-rails"
gem "turbo-rails"
gem "tzinfo-data", platforms: %i[windows jruby]
gem "web-console", group: :development

Gemfile.lock

GEM
  remote: https://rubygems.org/
  specs:
    actioncable (7.1.3.2)
      actionpack (= 7.1.3.2)
      activesupport (= 7.1.3.2)
      nio4r (~> 2.0)
      websocket-driver (>= 0.6.1)
      zeitwerk (~> 2.6)
    actionmailbox (7.1.3.2)
      actionpack (= 7.1.3.2)
      activejob (= 7.1.3.2)
      activerecord (= 7.1.3.2)
      activestorage (= 7.1.3.2)
      activesupport (= 7.1.3.2)
      mail (>= 2.7.1)
      net-imap
      net-pop
      net-smtp
    actionmailer (7.1.3.2)
      actionpack (= 7.1.3.2)
      actionview (= 7.1.3.2)
      activejob (= 7.1.3.2)
      activesupport (= 7.1.3.2)
      mail (~> 2.5, >= 2.5.4)
      net-imap
      net-pop
      net-smtp
      rails-dom-testing (~> 2.2)
    actionpack (7.1.3.2)
      actionview (= 7.1.3.2)
      activesupport (= 7.1.3.2)
      nokogiri (>= 1.8.5)
      racc
      rack (>= 2.2.4)
      rack-session (>= 1.0.1)
      rack-test (>= 0.6.3)
      rails-dom-testing (~> 2.2)
      rails-html-sanitizer (~> 1.6)
    actiontext (7.1.3.2)
      actionpack (= 7.1.3.2)
      activerecord (= 7.1.3.2)
      activestorage (= 7.1.3.2)
      activesupport (= 7.1.3.2)
      globalid (>= 0.6.0)
      nokogiri (>= 1.8.5)
    actionview (7.1.3.2)
      activesupport (= 7.1.3.2)
      builder (~> 3.1)
      erubi (~> 1.11)
      rails-dom-testing (~> 2.2)
      rails-html-sanitizer (~> 1.6)
    activejob (7.1.3.2)
      activesupport (= 7.1.3.2)
      globalid (>= 0.3.6)
    activemodel (7.1.3.2)
      activesupport (= 7.1.3.2)
    activerecord (7.1.3.2)
      activemodel (= 7.1.3.2)
      activesupport (= 7.1.3.2)
      timeout (>= 0.4.0)
    activestorage (7.1.3.2)
      actionpack (= 7.1.3.2)
      activejob (= 7.1.3.2)
      activerecord (= 7.1.3.2)
      activesupport (= 7.1.3.2)
      marcel (~> 1.0)
    activesupport (7.1.3.2)
      base64
      bigdecimal
      concurrent-ruby (~> 1.0, >= 1.0.2)
      connection_pool (>= 2.2.5)
      drb
      i18n (>= 1.6, < 2)
      minitest (>= 5.1)
      mutex_m
      tzinfo (~> 2.0)
    addressable (2.8.6)
      public_suffix (>= 2.0.2, < 6.0)
    ast (2.4.2)
    base64 (0.2.0)
    bigdecimal (3.1.7)
    bindex (0.8.1)
    bootsnap (1.18.3)
      msgpack (~> 1.2)
    builder (3.2.4)
    capybara (3.40.0)
      addressable
      matrix
      mini_mime (>= 0.1.3)
      nokogiri (~> 1.11)
      rack (>= 1.6.0)
      rack-test (>= 0.6.3)
      regexp_parser (>= 1.5, < 3.0)
      xpath (~> 3.2)
    concurrent-ruby (1.2.3)
    connection_pool (2.4.1)
    crass (1.0.6)
    date (3.3.4)
    debug (1.9.1)
      irb (~> 1.10)
      reline (>= 0.3.8)
    drb (2.2.1)
    erubi (1.12.0)
    ffi (1.16.3)
    globalid (1.2.1)
      activesupport (>= 6.1)
    hanami-router (0.6.2)
      hanami-utils (~> 0.7)
      http_router (~> 0.11)
    hanami-utils (0.9.2)
    http_router (0.11.2)
      rack (>= 1.0.0)
      url_mount (~> 0.2.1)
    i18n (1.14.4)
      concurrent-ruby (~> 1.0)
    image_processing (1.12.2)
      mini_magick (>= 4.9.5, < 5)
      ruby-vips (>= 2.0.17, < 3)
    importmap-rails (2.0.1)
      actionpack (>= 6.0.0)
      activesupport (>= 6.0.0)
      railties (>= 6.0.0)
    io-console (0.7.2)
    irb (1.12.0)
      rdoc
      reline (>= 0.4.2)
    json (2.7.1)
    language_server-protocol (3.17.0.3)
    lint_roller (1.1.0)
    litestack (0.4.3)
      erubi
      hanami-router
      oj
      rack
      sqlite3
      tilt
    loofah (2.22.0)
      crass (~> 1.0.2)
      nokogiri (>= 1.12.0)
    mail (2.8.1)
      mini_mime (>= 0.1.1)
      net-imap
      net-pop
      net-smtp
    marcel (1.0.4)
    matrix (0.4.2)
    mini_magick (4.12.0)
    mini_mime (1.1.5)
    minitest (5.22.3)
    msgpack (1.7.2)
    mutex_m (0.2.0)
    net-imap (0.4.10)
      date
      net-protocol
    net-pop (0.1.2)
      net-protocol
    net-protocol (0.2.2)
      timeout
    net-smtp (0.4.0.1)
      net-protocol
    nio4r (2.7.1)
    nokogiri (1.16.3-aarch64-linux)
      racc (~> 1.4)
    nokogiri (1.16.3-arm-linux)
      racc (~> 1.4)
    nokogiri (1.16.3-arm64-darwin)
      racc (~> 1.4)
    nokogiri (1.16.3-x86-linux)
      racc (~> 1.4)
    nokogiri (1.16.3-x86_64-darwin)
      racc (~> 1.4)
    nokogiri (1.16.3-x86_64-linux)
      racc (~> 1.4)
    oj (3.16.3)
      bigdecimal (>= 3.0)
    parallel (1.24.0)
    parser (3.3.0.5)
      ast (~> 2.4.1)
      racc
    propshaft (0.8.0)
      actionpack (>= 7.0.0)
      activesupport (>= 7.0.0)
      rack
      railties (>= 7.0.0)
    psych (5.1.2)
      stringio
    public_suffix (5.0.4)
    puma (6.4.2)
      nio4r (~> 2.0)
    racc (1.7.3)
    rack (3.0.10)
    rack-session (2.0.0)
      rack (>= 3.0.0)
    rack-test (2.1.0)
      rack (>= 1.3)
    rackup (2.1.0)
      rack (>= 3)
      webrick (~> 1.8)
    rails (7.1.3.2)
      actioncable (= 7.1.3.2)
      actionmailbox (= 7.1.3.2)
      actionmailer (= 7.1.3.2)
      actionpack (= 7.1.3.2)
      actiontext (= 7.1.3.2)
      actionview (= 7.1.3.2)
      activejob (= 7.1.3.2)
      activemodel (= 7.1.3.2)
      activerecord (= 7.1.3.2)
      activestorage (= 7.1.3.2)
      activesupport (= 7.1.3.2)
      bundler (>= 1.15.0)
      railties (= 7.1.3.2)
    rails-dom-testing (2.2.0)
      activesupport (>= 5.0.0)
      minitest
      nokogiri (>= 1.6)
    rails-html-sanitizer (1.6.0)
      loofah (~> 2.21)
      nokogiri (~> 1.14)
    railties (7.1.3.2)
      actionpack (= 7.1.3.2)
      activesupport (= 7.1.3.2)
      irb
      rackup (>= 1.0.0)
      rake (>= 12.2)
      thor (~> 1.0, >= 1.2.2)
      zeitwerk (~> 2.6)
    rainbow (3.1.1)
    rake (13.1.0)
    rdoc (6.6.3.1)
      psych (>= 4.0.0)
    regexp_parser (2.9.0)
    reline (0.4.3)
      io-console (~> 0.5)
    rexml (3.2.6)
    rubocop (1.62.1)
      json (~> 2.3)
      language_server-protocol (>= 3.17.0)
      parallel (~> 1.10)
      parser (>= 3.3.0.2)
      rainbow (>= 2.2.2, < 4.0)
      regexp_parser (>= 1.8, < 3.0)
      rexml (>= 3.2.5, < 4.0)
      rubocop-ast (>= 1.31.1, < 2.0)
      ruby-progressbar (~> 1.7)
      unicode-display_width (>= 2.4.0, < 3.0)
    rubocop-ast (1.31.2)
      parser (>= 3.3.0.4)
    rubocop-performance (1.20.2)
      rubocop (>= 1.48.1, < 2.0)
      rubocop-ast (>= 1.30.0, < 2.0)
    ruby-progressbar (1.13.0)
    ruby-vips (2.2.1)
      ffi (~> 1.12)
    rubyzip (2.3.2)
    selenium-webdriver (4.18.1)
      base64 (~> 0.2)
      rexml (~> 3.2, >= 3.2.5)
      rubyzip (>= 1.2.2, < 3.0)
      websocket (~> 1.0)
    sqlite3 (1.7.3-aarch64-linux)
    sqlite3 (1.7.3-arm-linux)
    sqlite3 (1.7.3-arm64-darwin)
    sqlite3 (1.7.3-x86-linux)
    sqlite3 (1.7.3-x86_64-darwin)
    sqlite3 (1.7.3-x86_64-linux)
    standard (1.35.1)
      language_server-protocol (~> 3.17.0.2)
      lint_roller (~> 1.0)
      rubocop (~> 1.62.0)
      standard-custom (~> 1.0.0)
      standard-performance (~> 1.3)
    standard-custom (1.0.2)
      lint_roller (~> 1.0)
      rubocop (~> 1.50)
    standard-performance (1.3.1)
      lint_roller (~> 1.1)
      rubocop-performance (~> 1.20.2)
    standardrb (1.0.1)
      standard
    stimulus-rails (1.3.3)
      railties (>= 6.0.0)
    stringio (3.1.0)
    tailwindcss-rails (2.3.0)
      railties (>= 6.0.0)
    tailwindcss-rails (2.3.0-aarch64-linux)
      railties (>= 6.0.0)
    tailwindcss-rails (2.3.0-arm-linux)
      railties (>= 6.0.0)
    tailwindcss-rails (2.3.0-arm64-darwin)
      railties (>= 6.0.0)
    tailwindcss-rails (2.3.0-x86_64-darwin)
      railties (>= 6.0.0)
    tailwindcss-rails (2.3.0-x86_64-linux)
      railties (>= 6.0.0)
    thor (1.3.1)
    tilt (2.3.0)
    timeout (0.4.1)
    turbo-rails (2.0.5)
      actionpack (>= 6.0.0)
      activejob (>= 6.0.0)
      railties (>= 6.0.0)
    tzinfo (2.0.6)
      concurrent-ruby (~> 1.0)
    unicode-display_width (2.5.0)
    url_mount (0.2.1)
      rack
    web-console (4.2.1)
      actionview (>= 6.0.0)
      activemodel (>= 6.0.0)
      bindex (>= 0.4.0)
      railties (>= 6.0.0)
    webrick (1.8.1)
    websocket (1.2.10)
    websocket-driver (0.7.6)
      websocket-extensions (>= 0.1.0)
    websocket-extensions (0.1.5)
    xpath (3.2.0)
      nokogiri (~> 1.8)
    zeitwerk (2.6.13)

PLATFORMS
  aarch64-linux
  arm-linux
  arm64-darwin
  x86-linux
  x86_64-darwin
  x86_64-linux

DEPENDENCIES
  bootsnap
  capybara
  debug
  image_processing (~> 1.2)
  importmap-rails
  litestack
  propshaft
  puma (>= 5.0)
  rails (~> 7.1.3, >= 7.1.3.2)
  selenium-webdriver
  sqlite3 (~> 1.4)
  standardrb
  stimulus-rails
  tailwindcss-rails
  turbo-rails
  tzinfo-data
  web-console

RUBY VERSION
   ruby 3.3.0p0

BUNDLED WITH
   2.5.3

I'm certainly doing something wrong... what is my error?

Running `rails db:prepare` creates a development database with test environment set

Usually running rails db:create creates both the development and test database. However, with litestack installed, only the development database is created in db/development, but no test database is created in db/test.

Manually setting a database path inside config/database.yml for the test database fixes this issue:

default: &default
  adapter: litedb
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  idle_timeout: 0
  database: <%= Litesupport.root.join("data.sqlite3") %>

development:
  <<: *default

test:
  <<: *default
  database: ./db/test/data.sqlite3 # Manually set path here

I assume the reason is that when the test database is created during rails db:create, the Litesupport.root path doesn't get updated and still uses the development path.

Steps to reproduce

> rails new litestackapp
> cd litestackapp
> bundle add litestack
> rails generate litestack:install
> rails db:prepare
> sqlite3 db/development/data.sqlite3 "select * from ar_internal_metadata"

Expected behavior

There should be a row with development environment

environment|development|2023-10-16 07:14:34.551049|2023-10-16 07:14:34.572026

Actual behavior

The environment is set to test instead:

environment|test|2023-10-16 07:14:34.551049|2023-10-16 07:14:34.572026

System configuration

Rails 7.1.1
Litestack 0.4.1 and master branch

Rescuing Exception instead of StandardError

Is there a specific, intentional reason that you rescue Exception instead of rescue StandardError. Rubocop and Standard will both warn that this is dangerous.

As noted in this thorough StackOverflow question:

Exception is the root of Ruby's exception hierarchy, so when you rescue Exception you rescue from everything, including subclasses such as SyntaxError, LoadError, and Interrupt.
-- https://stackoverflow.com/a/10048406/2884386

The recommendation is:

Use StandardError instead for general exception catching. When the original exception is re-raised (e.g. when rescuing to log the exception only), rescuing Exception is probably okay.

I'm curious if there are places where we should rescue StandardError, and if there are places that absolutely need to stay rescue Exception

Create GitHub workflow

  • run StandardRB to ensure consistent formatting
  • run tests against a matrix of Ruby versions, Rails versions, and SQLite3 versions

Under what condition is Litesupport::Connection forked?

Litesupport::Connection includes the Forkable module. I follow why we prepend the Forkable module to Process, but I don't yet follow how or when Litesupport::Connection would be forked and needs to manage that.

I want to understand the condition(s) so that I can hopefully write tests for these conditions.

Litesearch: has_many through

Hi! Really nice stack fo sqlite, congratulations.

I've been playing around with it and I'm wondering if Litesearch supports has_many :through associations. I've tried but I failed, all the examples are has_many -> belongs_to.

Cheers

Error raised when calling Rails.cache.clear

When I try to call Rails.cache.clear I have this error message

...active_support/cache/litecache.rb:51:in `clear': wrong number of arguments (given 1, expected 0) (ArgumentError)

I think the signature of method should be

-def clear()
+def clear(options = nil)
  @cache.clear
end

Readme typos

Hey, small typo in the readme:

Fulltext Search Server (e.g. Elasticsearch, Mielisearch)

is supposed to be Meilisearch

litedb can be used exactly as the SQLite3 gem, since litedb iherits from SQLite3

is supposed to be inherits

Thanks!

`Litejob` does not define `#perform_after`

The readme notes you can queue a job after a delay with #perform_after. This method is not defined. There is a method #perform_in that shows up in the example comments Litejob.rb, but it has a typo in it.

Active Storage service?

Hey, love the rationale behind the gem! I'm trying it out for some hobby projects and liking it so far.
And something I've always wondered is: why not use SQLite for blob storage? Raw performance looks pretty good

Is this on the roadmap? I'd love to help

Make sure requiring a single Litestack component loads only that component

require 'litestack'

currently loads every component in the library, this is OK, but we should make sure that something like:

require ''litestack/litecache

Will only load the files required to run litecache and not the rest of the library

Same should happen with any configuration that auto loads litestack components like the Rails env configuration like

config.active_job.queue_adapter = :litejob

How do I run the test suite for this project?

I dont seem to be able to run the test suite for this project.

  • Right away it complains about cannot find active_record so I add gem "rails" to the Gemfile to get past this
  • After that I get more errors coming from test/test_ar_search.rb (unrecognized option: "contentless_delete" (SQLite3::SQLException))
  • After skipping test/test_ar_search.rb then I receive more errors in test/test_litejob.rb (cannot load such file -- ../lib/litestack/litejobqueue)

Would like to be able to just type bundle exec rake test and have the test suite just work. However I cant help out with that until I get some info for steps here.

Can you please provide some instructions on how to run the test suite?

Add ActionText support for Litesearch

Since a lot of Rails apps nowadays rely on ActionText for rich text storage, it would be optimal to have native Litesearch support for such model attributes

Create git tags for all versions

Currently the diff links in the changelog are broken because we have not been creating git tags for the released versions, We need to start tagging the releases

This requirement was already stated in both #68 and #64 but somehow was ignored.

Here are the git tag commands you need to run to tag all prior releases

GIT_COMMITTER_DATE="$(git show --format=%aD b43964d0c220efcfcc1654a864c6a046666c25e4)" git tag v0.1.0 b43964d0c220efcfcc1654a864c6a046666c25e4

GIT_COMMITTER_DATE="$(git show --format=%aD 009ddb94a036fbdfaa36ece66a5dc3e2fe21a683)" git tag v0.1.1 009ddb94a036fbdfaa36ece66a5dc3e2fe21a683

GIT_COMMITTER_DATE="$(git show --format=%aD 0298c963ebc6255d60e0b6b89576cb65abb39867)" git tag v0.1.2 0298c963ebc6255d60e0b6b89576cb65abb39867

GIT_COMMITTER_DATE="$(git show --format=%aD 4593763d2692f4b6968db60cfec23754f348e9e4)" git tag v0.1.3 4593763d2692f4b6968db60cfec23754f348e9e4

GIT_COMMITTER_DATE="$(git show --format=%aD 4340ec939bc8093faa61d4800ad612baa022967a)" git tag v0.1.4 4340ec939bc8093faa61d4800ad612baa022967a

GIT_COMMITTER_DATE="$(git show --format=%aD 73e0e21fc79ca0dd26222452b1c734607bf00c7d)" git tag v0.1.5 73e0e21fc79ca0dd26222452b1c734607bf00c7d

GIT_COMMITTER_DATE="$(git show --format=%aD 8b454ea01411c935e0d330a085b20e30d642128a)" git tag v0.1.6 8b454ea01411c935e0d330a085b20e30d642128a

GIT_COMMITTER_DATE="$(git show --format=%aD c447dda5bc49e875371348be1b08eca09b170015)" git tag v0.1.7 c447dda5bc49e875371348be1b08eca09b170015

GIT_COMMITTER_DATE="$(git show --format=%aD b43964d0c220efcfcc1654a864c6a046666c25e4)" git tag v0.1.8 b43964d0c220efcfcc1654a864c6a046666c25e4

GIT_COMMITTER_DATE="$(git show --format=%aD 057c8455b70da1d990e7a7c25eb9919265c549eb)" git tag v0.1.9 057c8455b70da1d990e7a7c25eb9919265c549eb

GIT_COMMITTER_DATE="$(git show --format=%aD 3ff509a817809991680320a2ad35dc2547b4c7a8)" git tag v0.2.0 3ff509a817809991680320a2ad35dc2547b4c7a8

GIT_COMMITTER_DATE="$(git show --format=%aD 21409d2a21546191f37c864244d770b55ace16ce)" git tag v0.2.1 21409d2a21546191f37c864244d770b55ace16ce

GIT_COMMITTER_DATE="$(git show --format=%aD d135e589821502a296dbebfe086f0f21cb252abc)" git tag v0.2.2 d135e589821502a296dbebfe086f0f21cb252abc

GIT_COMMITTER_DATE="$(git show --format=%aD 06495c9102998482061d629c5e17ebfc5aae3fe6)" git tag v0.2.3 06495c9102998482061d629c5e17ebfc5aae3fe6

GIT_COMMITTER_DATE="$(git show --format=%aD 015e9f99793f933670b0ab035ae53c7c632b4e90)" git tag v0.2.5 015e9f99793f933670b0ab035ae53c7c632b4e90

GIT_COMMITTER_DATE="$(git show --format=%aD 41b0413f3fc25735962ac4e11ea8d51d22cf88e9)" git tag v0.2.6 41b0413f3fc25735962ac4e11ea8d51d22cf88e9

GIT_COMMITTER_DATE="$(git show --format=%aD e96ed83f23bc61c83a80f5a8105d77c195cf084e)" git tag v0.3.0 e96ed83f23bc61c83a80f5a8105d77c195cf084e

GIT_COMMITTER_DATE="$(git show --format=%aD 2f51e5e902630cbe7564fd1dc33f79c8bfa1a00b)" git tag v0.4.0 2f51e5e902630cbe7564fd1dc33f79c8bfa1a00b

GIT_COMMITTER_DATE="$(git show --format=%aD 1c4bf8134e165295604f71bb64d77ebb200c9ab5)" git tag v0.4.1 1c4bf8134e165295604f71bb64d77ebb200c9ab5

GIT_COMMITTER_DATE="$(git show --format=%aD 8e4f81addd5bd9bbe3348a78eb7a802cb9799ee1)" git tag v0.4.2 8e4f81addd5bd9bbe3348a78eb7a802cb9799ee1

After running all these commands you can push up the tags with

git push origin --tags

Going forward we want to make sure we continue to tag the version releases as any well maintained gem would

Liteboard SQLite3::CantOpenException

First I want to thank you for this project, and all the hard work being put into it.

Issue: I am trying to get liteboard running, but running into and sqlite error when trying to start the server. The error is /Users/.../.rvm/gems/ruby-3.2.2/gems/sqlite3-1.6.8-x86_64-darwin/lib/sqlite3/database.rb:105:in 'open_v2': unable to open database file (SQLite3::CantOpenException)

Reproduction steps:

  1. Launch a separate terminal and navigate to the root of the app
  2. I enter `liteboard -d ./db/development -E development
  3. get error

I know you are working on the documentation for the liteboard, from a previous ticket, but thought I would try it and see if I can get it going. :) From the error I don't think it is strictly a litestack issue, but maybe I am missing a step so thought I would post here. Any help/guidance you can provide is greatly appreciated. Other then this issue it has been going good using this gem.

Litequeue#count doesn't appear to work as expected

On my machine, the Litequeue#count method doesn't work as expected. It always returns the count of the table, even when you pass in a queue name. Below is a reproducible bug script to demonstrate the problem:

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  gem "sqlite3"
end

require "sqlite3"

$db = SQLite3::Database.new(":memory:")
$db.execute <<-SQL
  CREATE TABLE queues(
    name TEXT,
    value TEXT
  );
SQL

class WhereIIFTest < Minitest::Test
  def test_where_iif
    $db.execute("INSERT INTO queues(name, value) VALUES (?, ?), (?, ?)", ['default', 1], ['urgent', 10])
    
    where_iif = "SELECT COUNT(*) FROM queues WHERE iif(?, name = ?, 1)"
    
    assert_equal 2, $db.get_first_value(where_iif, nil)
    assert_equal 1, $db.get_first_value(where_iif, 'default')
    assert_equal 1, $db.get_first_value(where_iif, 'urgent')
  end
end

How to configure path for Action Cable DB

Hello

I am trying to use the Action Cable adapter.
I need to put the database in /storage/cable.db as this is my persisted volume

I have tried to add a litecable.yml file both in the root and config/ with no luck

here is the WIP PR as the project is open source adrienpoly/rubyvideo#45

thanks

Struggling to get LiteJob/LiteQueue workers to run

I've got a pretty simple Rails 7.1 / Ruby 3.2.2 app on litestack 0.4.3.

I can see that jobs are being enqueued for both ActiveJob jobs and ActionMailer emails queued with deliver_later.

The app is configured with config.active_job.queue_adapter = :litejob and config.active_job.queue_name_prefix = Rails.env.

I've got a very simple litejob.yml which configures a few queues, taken from the docs, and the config displayed in Litejobqueue.jobqueue looks correct, as best as I can tell;

irb(main):003> Litejobqueue.jobqueue
=> 
#<Litejobqueue:0x00007f012f4e5a98
 @conn=
  #<Litesupport::Pool:0x00007f012f505320
   @block=
    #<Proc:0x00007f012f4e52a0 /usr/local/bundle/ruby/3.2.0/gems/litestack-0.4.3/lib/litestack/litesupport.rb:235>,
   @count=1,
   @mutex=#<Litesupport::Mutex:0x00007f012f4e5188 @mutex=#<Thread::Mutex:0x00007f012f4e5110>>,
   @resources=#<Thread::Queue:0x00007f012f4e5228>>,
 @gc=
  #<Thread:0x00007f012f4e16a0 /usr/local/bundle/ruby/3.2.0/gems/litestack-0.4.3/lib/litestack/litejobqueue.rb:214 sleep>,
 @jobs_in_flight=0,
 @logger=
  #<Logger:0x00007f012f4e1fb0
   @default_formatter=#<Logger::Formatter:0x00007f012f4e1e70 @datetime_format=nil>,
   @formatter=nil,
   @level=0,
   @logdev=nil,
   @progname=nil>,
 @mutex=#<Litesupport::Mutex:0x00007f012f4e1588 @mutex=#<Thread::Mutex:0x00007f012f4e1560>>,
 @options=
  {:config_path=>"./config/litejob.yml",
   :path=>#<Pathname:./storage/production/queue.sqlite3>,
   :queues=>[["default", 1], ["urgent", 5], ["critical", 10]],
   :workers=>5,
   :retries=>5,
   :retry_delay=>60,
   :retry_delay_multiplier=>10,
   :dead_job_retention=>864000,
   :gc_sleep_interval=>7200,
   :logger=>nil,
   :sleep_intervals=>[0.001, 0.005, 0.025, 0.125, 0.625, 1.0, 2.0],
   :metrics=>false},
 @queues=[[10, [["critical", false]]], [5, [["urgent", false]]], [1, [["default", false]]]],
 @running=true,
 @workers=
  [#<Thread:0x00007f012f4e1bc8 /usr/local/bundle/ruby/3.2.0/gems/litestack-0.4.3/lib/litestack/litejobqueue.rb:182 sleep>,
   #<Thread:0x00007f012f4e1ab0 /usr/local/bundle/ruby/3.2.0/gems/litestack-0.4.3/lib/litestack/litejobqueue.rb:182 sleep>,
   #<Thread:0x00007f012f4e19c0 /usr/local/bundle/ruby/3.2.0/gems/litestack-0.4.3/lib/litestack/litejobqueue.rb:182 sleep>,
   #<Thread:0x00007f012f4e18d0 /usr/local/bundle/ruby/3.2.0/gems/litestack-0.4.3/lib/litestack/litejobqueue.rb:182 sleep>,
   #<Thread:0x00007f012f4e17e0 /usr/local/bundle/ruby/3.2.0/gems/litestack-0.4.3/lib/litestack/litejobqueue.rb:182 sleep>]>

That looks to me like the workers are setup but none of the jobs in the queues are being worked off at all.

Is there something super obvious I'm missing?

Update: I forgot to mention that this is also the case in development mode as well; I can successfully enqueue jobs but they're never picked up by the workers.

find_in_batches stalls

So using rails 7.1.1 and activerecord with litedb and running this:

Model.find_in_batches do |batch|
  puts "batch"
end

Seems to stall after the first iteration of the loop, I've tried the other batch operators and they also stall. I'm not really sure what to make of this as it seems like it should work.

Intention of passing @mutex to Litesupport.synchronize?

In the Litejobqueue, calls to Litesupport.synchronize pass the local @mutex instance variable:

def job_started
Litesupport.synchronize(@mutex){@jobs_in_flight += 1}
end
def job_finished
Litesupport.synchronize(@mutex){@jobs_in_flight -= 1}
end

This Litesupport.synchronize method accepts a fiber_sync optional argument, but does nothing with it:

def self.synchronize(fiber_sync = false, &block)
if self.scheduler == :fiber or self.scheduler == :polyphony
yield # do nothing, just run the block as is
else
self.mutex.synchronize(&block)
end
end

So, we have code that passes a mutex that isn't used to a method that accepts an optional argument that isn't used. I'm not sure the core intent, so I'm not sure how to open a PR to resolve this confusion.

Add documentation to readme for litemetric and liteboard

Seems there are no references to liteboard in the readme.

There is only 1 quick reference to litemetric in the readme doesnt explain anything.

Would be nice to have some descriptions and links to more documentation in the readme.

Extend Litesearch::Index with a #similar method to fetch similar records

Litesearch can be extended to provide a #similar method with the following signature Litesearch::Index#similar(id, limit=DEFAULT_LIMIT)

This method will create a representation of the document referred to by id in the following manner:

  • All tokens in the doc will be fetched, numeric only tokens ignored
  • Remaining terms will be sorted by how popular they are in the corpus (least popular is most important), only top 25 will be taken into account
  • A search query will be constructed by ORing all the remaining terms
  • The query will be ran against the corpus and the results will be ordered by the search_rank which, in this case, will be an approximation of similarity

Example

idx = db.search_index('idx')
idx.similar(100, 10) # return the top 10 matching docs to doc with id = 100;

AR & Sequel Integration

AR and Sequel models will be also extended with a #similar method with the following signature Model#similar(limit=DEFAULT_LIMIT)

Usage should be as easy as

book = Book.find(100)
book.similar # top 10 similar books

Not getting ActionCable connected() when using litecable adapter

In development when I switch to adapter: litecable in config/cable.yml then it is not calling the connected() function. It does successfully call the initialized() function.

Everything works correctly when I switch back to adapter: async where I get connected() and received()

I am using puma gem for development server on Rails 7.1. Let me know if there is any other information that would be helpful.

Remove jobs enqueued with perform_later

I'd like to be able to:

  • Inspect the queue for jobs scheduled to be performed later by matching class and arguments
  • Remove jobs from the queue that are scheduled to be performed later by matching class and arguments

Is there an easy way to implement this in my app code? I think this would need to be a new set of SQL statements?

Increase time precision by using UNIXEPOCH('subsec') instead of UNIXEPOCH()

As of version 3.42.0 (released on 2023-05-16), the UNIXEPOCH function now accepts a subsec modifier, which "increases the resolution from seconds to milliseconds" (source: https://www.sqlite.org/lang_datefunc.html#subsec).

By having millisecond precision, instead of only second precision, we allow for finer-grained performance. For example, in tests, everything is slow because a retry will have to wait at least 1 second.

If we want to support up to version 3.37, maybe we should register a custom function that either uses the 'subsec' modifier with UNIXEPOCH, if available, or falls back to the (JULIANDAY('now') - 2440587.5)*86400.0 function suggested by the docs (source: https://www.sqlite.org/lang_datefunc.html#examples). Either way, I truly believe that millisecond precision is needed.

Clean test/ directory

  • ensure only Minitest test files are in the directory
  • create a /scripts directory to hold the non-Minitest files

[Docs] List of disadvantages

Not sure if an issue is the best place for this so let me know.

I am considering using litestack for my next project, and it would be nice if there was a good list of the disadvantages of using it.

I think it's valuable have a source of well known limitations and drawbacks of this library so that there are better user expectations.

It would also be good to have as a community resource to help avoid any common footguns when deploying.

Is this already available anywhere?

Liteboard does not work alongside Rack 3

After creating a new Rails 7.1 application and running the liteboard executable,

bundle exec liteboard -d path/to/data

I see the following error when trying to visit the app at http://localhost:9292:

NoMethodError: undefined method `unescape' for module URI

This error is reproducible with the following dependencies:

Ruby version: 3.3.0
Rails version: 7.1.2
Rack version: 3.0.8
Litestack version: 0.4.2

With this set of dependencies, litestack installs an old version of hanami-router: 0.6.2, whereas the most recent version of hanami-router as of this writing is 2.0.x. The 0.6.x version of hanami-router is not compatible with Ruby 3, hence the error with the URI module. I believe the hanami-router 0.6.x is installed because it does not constrain the Rack version; Bundler happily picks this version to live alongside Rack 3 in my Rails application since latest version of hanami-router is constrained to Rack 2.x.

I've opened an issue with hanami-router to inquire about support for Rack 3. In the meantime, a possible fix in Litestack would be to match the rack constraint with rack ~> 2 or rack < 3.

0.41 code not published

I was trying to run #41 to test it and I am getting this error when I start my app

LoadError: cannot load such file -- /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/bundler/gems/litestack-ca27a3d865ee/lib/litestack/litesearch/model

I have the same error running from master

I don't have the error when I use the latest version from RubyGems

I think some code is not published on master as RubyGems version is 0.41 and on master it is 0.40

`hiredis` not in gemspec, attempted to load from `require 'litestack'`.

I was just going to give Litestack a whirl, but it requires hiredis in litestack/litesupport:

require 'hiredis'

which itself is required in litestack.rb:
require_relative "litestack/litesupport"

Looking through the code, it appears it's used for benchmarks and not actually required by the lib itself.

/tmp/litestack-demo
โ–ถ gem install litestack
Successfully installed litestack-0.1.7
Parsing documentation for litestack-0.1.7
Done installing documentation for litestack after 0 seconds
1 gem installed

/tmp/litestack-demo
โ–ถ pry
[1] pry(main)> require 'litestack'
LoadError: cannot load such file -- hiredis
from <internal:/Users/ttilberg/.asdf/installs/ruby/3.2.0/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:88:in `require'
Caused by LoadError: cannot load such file -- litestack
from <internal:/Users/ttilberg/.asdf/installs/ruby/3.2.0/lib/ruby/site_ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:88:in `require'

Move shared connection code to Litedb

Ensure different Litestack components rely on Litedb rather than vanilla SQLite3 connections, move some of the shared logic in Litesupport and some of the repeated code sprinkled over the various components to Litedb instead

Cannot configure Litejobqueue queues

In short, setup is called before @queueus get's properly setup, meaning that workers are started without queues.

To lay out the story more fully:

Let's imagine you initialize a Litejobqueue like so:

jobqueue = Litejobqueue.new({
  path: ":memory:",
  retries: 2,
  retry_delay: 1,
  retry_delay_multiplier: 1,
  # gc_sleep_interval: 1,
  # dead_job_retention: 1,
  # logger: 'STDOUT',
  queues: [
    ['test', 1]
  ]
})

Litejobqueue.new will call

def self.new(options = {})
return @@queue if @@queue
@@queue = allocate
@@queue.send(:initialize, options)
@@queue
end

At line 63 Litejobqueue#initialize is called, which will run

def initialize(options = {})
@queues = [] # a place holder to allow workers to process
super(options)
# group and order queues according to their priority
pgroups = {}
@options[:queues].each do |q|
pgroups[q[1]] = [] unless pgroups[q[1]]
pgroups[q[1]] << [q[0], q[2] == "spawn"]
end
@queues = pgroups.keys.sort.reverse.collect{|p| [p, pgroups[p]]}
collect_metrics if @options[:metrics]
end

The super at line 73 is calling Litequeue#initialize, which is simply

def initialize(options = {})
init(options)
end

This init method called actually comes from the included Litesupport::Liteconnection module and is defined as

def init(options = {})
#c configure the object, loading options from the appropriate location
configure(options)
# setup connections and background threads
setup
# handle process exiting
at_exit do
exit_callback
end
# handle forking (restart connections and background threads)
Litesupport::ForkListener.listen do
setup
end
end

The setup call at line 271 will call the Litejobqueue#setup method:

def setup
super
@jobs_in_flight = 0
@workers = @options[:workers].times.collect{ create_worker }
@gc = create_garbage_collector
@mutex = Litesupport::Mutex.new
end

At line 160 we see the call to Litejobqueue#create_worker which will spawn our worker threads/fibers/whatever.

The issue is that when

@queues.each do |level| # iterate through the levels
runs and inspects the @queues instance variable, we are still within the execution context of Litejobqueue#initialize line 73. The @queues instance variable won't be populated until line 81.

You can verify this with a simple puts in the code.


The core pain point here, for me, is that it hinders my ability to set up tests. While I can understand well why this is happening, I am not yet confident in how best to fix things to resolve this issue. Happy to help with some guidance tho.

[Question] Is it possible to add support for Recurring Tasks?

First of all, thank you very much for the gem. I am using it for my personal project and hosting it on Fly.io with LiteFS just works.

I am not very familiar with how all these works together, so please bear with me if the question sounds stupid.

One thing I am missing now with the setup is the ability to be able to run cron jobs. I see SolidQueue recently merged in Recurring tasks which was inspired by GoodJob's cron manager, and I am wondering if it's possible to have something like that in Litejob?

What I have looked into:

  1. Try out solid queue with Fly.io + LiteFS, but I just couldn't set it up. There weren't many resources out there so I gave up.
  2. Setting up cron on Fly.io, there are a few suggestions on scheduled machine and Crontab with Supercronic, but those require extra machine.

Allow configuration of litedb root path

I'm brand new to Litestack, I've just been playing with Rails + sqlite in production manually (setting the journal mode to WAL).

It would be nice if we could configure the Litesupport root outside of using an env variable so that I don't have to add yet another env variable to production, and it works the same as dev/staging.

What about using a Rails config initializer? If you are open to this idea, let's hash it out and then I'll create a PR for it.

Creating a new repo causes an error when running `bin/rails dbconsole`

I ran the following commands and ran into a dbconsole error.

  1. I generated a brand new rails repo using ruby 3.2.1 and rails 7.1.3.
  2. bundle add litestack.
  3. bundle install litestack.
  4. rails generate litestack:install
  5. bin/rails dbconsole
/Users/herk/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/railties-7.1.3/lib/rails/commands/dbconsole/dbconsole_command.rb:71:in `find_cmd_and_exec': undefined method `find_cmd_and_exec' for #<ActiveRecord::ConnectionAdapters::LitedbAdapter:0x0000000105cd0658 @transaction_manager=#<ActiveRecord::ConnectionAdapters::TransactionManager:0x0000000105dba5a0 @stack=[], @connection=#<ActiveRecord::ConnectionAdapters::LitedbAdapter:0x0000000105cd0658 ...>, @has_unmaterialized_transactions=false, @materializing_transactions=false, @lazy_transactions_enabled=true>, @query_cache={}, @query_cache_enabled=false, @query_cache_max_size=100, @raw_connection=#<Litedb:0x0000000105a3e0e8 @tracefunc=nil, @authorizer=nil, @encoding=#<Encoding:UTF-8>, @busy_handler=#<Proc:0x0000000105a3dd50 /Users/herk/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/litestack-0.4.2/lib/litestack/litedb.rb:110>, @collations={}, @functions={}, @results_as_hash=true, @type_translation=nil, @type_translator=#<Proc:0x00000001046fecd0 /Users/herk/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/sqlite3-1.7.0-arm64-darwin/lib/sqlite3/database.rb:747 (lambda)>, @readonly=false, @default_transaction_mode=:deferred, @running=true, @collecting_metrics=nil>, @unconfigured_connection=nil, @logger=#<ActiveSupport::BroadcastLogger:0x0000000104e96330 @broadcasts=[#<ActiveSupport::Logger:0x00000001052f3d60 @level=0, @progname=nil, @default_formatter=#<Logger::Formatter:0x0000000104e9ab60 @datetime_format=nil>, @formatter=#<ActiveSupport::Logger::SimpleFormatter:0x0000000104e96a38 @datetime_format=nil, @thread_key="activesupport_tagged_logging_tags:14780">, @logdev=#<Logger::LogDevice:0x00000001052f4800 @shift_period_suffix="%Y%m%d", @shift_size=104857600, @shift_age=1, @filename="/Users/herk/Projects/broken_litestack/log/development.log", @dev=#<File:/Users/herk/Projects/broken_litestack/log/development.log>, @binmode=false, @mon_data=#<Monitor:0x0000000104e9a8b8>, @mon_data_owner_object_id=4820>, @local_level_key=:logger_thread_safe_level_14760>], @progname="Broadcast", @formatter=#<ActiveSupport::Logger::SimpleFormatter:0x0000000104e96a38 @datetime_format=nil, @thread_key="activesupport_tagged_logging_tags:14780">>, @config={:adapter=>"litedb", :pool=>5, :idle_timeout=>0, :database=>"/Users/herk/Projects/broken_litestack/db/development/data.sqlite3", :strict=>true}, @connection_parameters={:adapter=>"litedb", :pool=>5, :idle_timeout=>0, :database=>"/Users/herk/Projects/broken_litestack/db/development/data.sqlite3", :strict=>true, :results_as_hash=>true}, @owner=#<Thread:0x000000010047b110 run>, @instrumenter=#<ActiveSupport::Notifications::Instrumenter:0x00000001055f42a8 @id="b3b75dbfd3570b886693", @notifier=#<ActiveSupport::Notifications::Fanout (19 patterns)>>, @pool=#<ActiveRecord::ConnectionAdapters::ConnectionPool:0x0000000105c906e8 @mon_data=#<Monitor:0x00000001055b5008>, @mon_data_owner_object_id=14720, @query_cache_enabled=#<Concurrent::Map:0x00000001055b4e28 entries=0 default_proc=#<Proc:0x00000001055b4dd8 /Users/herk/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/activerecord-7.1.3/lib/active_record/connection_adapters/abstract/query_cache.rb:35>>, @pool_config=#<ActiveRecord::ConnectionAdapters::PoolConfig:0x0000000105eb8218 @_mutex=#<Thread::Mutex:0x00000001055f5ae0>, @connection_class=ActiveRecord::Base, @db_config=#<ActiveRecord::DatabaseConfigurations::HashConfig:0x00000001055fb3f0 @env_name="development", @name="primary", @configuration_hash={:adapter=>"litedb", :pool=>5, :idle_timeout=>0, :database=>"./db/development/data.sqlite3"}>, @role=:writing, @shard=:default, @pool=#<ActiveRecord::ConnectionAdapters::ConnectionPool:0x0000000105c906e8 ...>, @schema_reflection=#<ActiveRecord::ConnectionAdapters::SchemaReflection:0x00000001059fcdc8 @cache=#<ActiveRecord::ConnectionAdapters::SchemaCache:0x0000000105dba3c0 @columns={}, @columns_hash={}, @primary_keys={}, @data_sources={}, @indexes={}, @database_version=#<ActiveRecord::ConnectionAdapters::AbstractAdapter::Version:0x0000000105b1d108 @version=[3, 44, 2], @full_version_string=nil>, @version=nil>, @cache_path="db/schema_cache.yml">>, @db_config=#<ActiveRecord::DatabaseConfigurations::HashConfig:0x00000001055fb3f0 @env_name="development", @name="primary", @configuration_hash={:adapter=>"litedb", :pool=>5, :idle_timeout=>0, :database=>"./db/development/data.sqlite3"}>, @role=:writing, @shard=:default, @checkout_timeout=5.0, @idle_timeout=nil, @size=5, @thread_cached_conns=#<Concurrent::Map:0x00000001055b4928 entries=1 default_proc=nil>, @connections=[#<ActiveRecord::ConnectionAdapters::LitedbAdapter:0x0000000105cd0658 ...>], @automatic_reconnect=true, @now_connecting=0, @threads_blocking_new_connections=0, @available=#<ActiveRecord::ConnectionAdapters::ConnectionPool::ConnectionLeasingQueue:0x0000000105eb43e8 @lock=#<ActiveRecord::ConnectionAdapters::ConnectionPool:0x0000000105c906e8 ...>, @cond=#<MonitorMixin::ConditionVariable:0x00000001055b4680 @monitor=#<Monitor:0x00000001055b5008>, @cond=#<Thread::ConditionVariable:0x00000001055b4608>>, @num_waiting=0, @queue=[]>, @lock_thread=false, @async_executor=nil, @reaper=#<ActiveRecord::ConnectionAdapters::ConnectionPool::Reaper:0x00000001055b4450 @pool=#<ActiveRecord::ConnectionAdapters::ConnectionPool:0x0000000105c906e8 ...>, @frequency=60.0>>, @idle_since=1208409.306946, @visitor=#<Arel::Visitors::SQLite:0x00000001059fdfc0 @dispatch={}, @connection=#<ActiveRecord::ConnectionAdapters::LitedbAdapter:0x0000000105cd0658 ...>>, @statements=#<ActiveRecord::ConnectionAdapters::SQLite3Adapter::StatementPool:0x00000001059fdac0 @cache={}, @statement_limit=1000>, @lock=ActiveSupport::Concurrency::NullLock, @prepared_statements=true, @advisory_locks_enabled=true, @default_timezone=nil, @raw_connection_dirty=false, @verified=nil, @memory_database=false, @use_insert_returning=true, @schema_cache=#<ActiveRecord::ConnectionAdapters::BoundSchemaReflection:0x00000001059fccb0 @schema_reflection=#<ActiveRecord::ConnectionAdapters::SchemaReflection:0x00000001059fcdc8 @cache=#<ActiveRecord::ConnectionAdapters::SchemaCache:0x0000000105dba3c0 @columns={}, @columns_hash={}, @primary_keys={}, @data_sources={}, @indexes={}, @database_version=#<ActiveRecord::ConnectionAdapters::AbstractAdapter::Version:0x0000000105b1d108 @version=[3, 44, 2], @full_version_string=nil>, @version=nil>, @cache_path="db/schema_cache.yml">, @connection=#<ActiveRecord::ConnectionAdapters::LitedbAdapter:0x0000000105cd0658 ...>>> (NoMethodError)

        ActiveRecord::Base.connection.find_cmd_and_exec(commands, *args)
                                     ^^^^^^^^^^^^^^^^^^
	from /Users/herk/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/litestack-0.4.2/lib/railties/rails/commands/dbconsole.rb:50:in `start'
	from /Users/herk/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/railties-7.1.3/lib/rails/commands/dbconsole/dbconsole_command.rb:9:in `start'
	from /Users/herk/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/railties-7.1.3/lib/rails/commands/dbconsole/dbconsole_command.rb:93:in `perform'
	from /Users/herk/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/thor-1.3.0/lib/thor/command.rb:28:in `run'
	from /Users/herk/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/thor-1.3.0/lib/thor/invocation.rb:127:in `invoke_command'
	from /Users/herk/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/railties-7.1.3/lib/rails/command/base.rb:178:in `invoke_command'
	from /Users/herk/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/thor-1.3.0/lib/thor.rb:527:in `dispatch'
	from /Users/herk/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/railties-7.1.3/lib/rails/command/base.rb:73:in `perform'
	from /Users/herk/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/railties-7.1.3/lib/rails/command.rb:71:in `block in invoke'
	from /Users/herk/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/railties-7.1.3/lib/rails/command.rb:149:in `with_argv'
	from /Users/herk/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/railties-7.1.3/lib/rails/command.rb:69:in `invoke'
	from /Users/herk/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/railties-7.1.3/lib/rails/commands.rb:18:in `<main>'
	from <internal:/Users/herk/.rbenv/versions/3.2.1/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
	from <internal:/Users/herk/.rbenv/versions/3.2.1/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
	from /Users/herk/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/bootsnap-1.17.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
	from bin/rails:4:in `<main>'

Can anyone else replicate this? It seems like the fix would be to swap find_cmd_and_exec(...) in db_console.rb with ActiveRecord::Base.connection.class.find_cmd_and_exec.

It looks like the DBConsole method will be removed in Rails 7.2. However, in older implementations, find_cmd_and_exec in the rails repo dbconsole_command.rb causes an error. This happens because find_cmd_and_exec is being applied to the instance ActiveRecord::Base.connection rather than the class when the method is defined as a class method in AbstractAdapter.

[v0.2.1] SQLite3::CantOpenException: unable to open database file

Hello

Thanks for your work on this Gem, very excited about it.
On a test project, I upgraded from 0.1.8 to 0.2.1 and I am getting this error whenever I try to start the app

SQLite3::CantOpenException: unable to open database file

It is a blank project using Rails 7.1 (main)

here is the stack trace rails s /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/sqlite3-1.6.2-arm64-darwin/lib/sqlite3/database.rb:90:in `open_v2': unable to open database file (SQLite3::CantOpenException) from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/sqlite3-1.6.2-arm64-darwin/lib/sqlite3/database.rb:90:in `initialize' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litesupport.rb:284:in `new' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litesupport.rb:284:in `create_connection' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litequeue.rb:109:in `create_connection' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litesupport.rb:279:in `block in create_pooled_connection' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litesupport.rb:146:in `block (2 levels) in initialize' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litesupport.rb:115:in `block in synchronize' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litesupport.rb:115:in `synchronize' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litesupport.rb:115:in `synchronize' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litesupport.rb:146:in `block in initialize' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litesupport.rb:145:in `times' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litesupport.rb:145:in `initialize' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litesupport.rb:279:in `new' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litesupport.rb:279:in `create_pooled_connection' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litesupport.rb:252:in `setup' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litejobqueue.rb:153:in `setup' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litesupport.rb:218:in `init' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litequeue.rb:40:in `initialize' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litejobqueue.rb:73:in `initialize' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litejobqueue.rb:63:in `new' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litejobqueue.rb:57:in `block in jobqueue' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litesupport.rb:115:in `block in synchronize' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litesupport.rb:115:in `synchronize' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litesupport.rb:115:in `synchronize' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litesupport.rb:91:in `synchronize' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litejobqueue.rb:57:in `jobqueue' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litejob.rb:89:in `get_jobqueue' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack/litejob.rb:46:in `included' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/active_job/queue_adapters/litejob_adapter.rb:43:in `include' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/active_job/queue_adapters/litejob_adapter.rb:43:in `' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/active_job/queue_adapters/litejob_adapter.rb:32:in `' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/active_job/queue_adapters/litejob_adapter.rb:14:in `' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/active_job/queue_adapters/litejob_adapter.rb:9:in `' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/active_job/queue_adapters/litejob_adapter.rb:8:in `' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack.rb:17:in `require_relative' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/litestack-0.2.1/lib/litestack.rb:17:in `' from :37:in `require' from :37:in `require' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/bootsnap-1.16.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/zeitwerk-2.6.8/lib/zeitwerk/kernel.rb:38:in `require' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/site_ruby/3.2.0/bundler/runtime.rb:60:in `block (2 levels) in require' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/site_ruby/3.2.0/bundler/runtime.rb:55:in `each' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/site_ruby/3.2.0/bundler/runtime.rb:55:in `block in require' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/site_ruby/3.2.0/bundler/runtime.rb:44:in `each' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/site_ruby/3.2.0/bundler/runtime.rb:44:in `require' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/site_ruby/3.2.0/bundler.rb:196:in `require' from /Users/adrien/code/projects/maneki/config/application.rb:19:in `' from :37:in `require' from :37:in `require' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/bootsnap-1.16.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/bundler/gems/rails-80709bfe7a4c/railties/lib/rails/commands/server/server_command.rb:139:in `block in perform' from :90:in `tap' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/bundler/gems/rails-80709bfe7a4c/railties/lib/rails/commands/server/server_command.rb:136:in `perform' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/thor-1.2.1/lib/thor/command.rb:27:in `run' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/thor-1.2.1/lib/thor/invocation.rb:127:in `invoke_command' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/bundler/gems/rails-80709bfe7a4c/railties/lib/rails/command/base.rb:179:in `invoke_command' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/thor-1.2.1/lib/thor.rb:392:in `dispatch' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/bundler/gems/rails-80709bfe7a4c/railties/lib/rails/command/base.rb:74:in `perform' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/bundler/gems/rails-80709bfe7a4c/railties/lib/rails/command.rb:71:in `block in invoke' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/bundler/gems/rails-80709bfe7a4c/railties/lib/rails/command.rb:149:in `with_argv' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/bundler/gems/rails-80709bfe7a4c/railties/lib/rails/command.rb:69:in `invoke' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/bundler/gems/rails-80709bfe7a4c/railties/lib/rails/commands.rb:18:in `' from :37:in `require' from :37:in `require' from /Users/adrien/.asdf/installs/ruby/3.2.0/lib/ruby/gems/3.2.0/gems/bootsnap-1.16.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require' from bin/rails:4:in `'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.