Last active
May 17, 2022 06:53
-
-
Save simi/2789ef5138608b0f18c181d921a08338 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# INFO: works well with Ruby 3.0.1 | |
# hangs on any Ruby including https://github.com/ruby/ruby/commit/f9196de1dee6f5ab8b6fe115070b92775a3500fe | |
# since the yielding code in each method is running in different Fiber (due to be wrapped into Enumerator and next is used) | |
require "bundler/inline" | |
gemfile(true) do | |
source "https://rubygems.org" | |
git_source(:github) { |repo| "https://github.com/#{repo}.git" } | |
# Activate the gem you are reporting the issue against. | |
gem "activerecord", "~> 7.0.0" | |
gem "sqlite3" | |
end | |
require "active_record" | |
require "minitest/autorun" | |
require "logger" | |
require 'csv' | |
# This connection will do for database-independent bug reports. | |
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") | |
ActiveRecord::Base.logger = Logger.new(STDOUT) | |
ActiveRecord::Schema.define do | |
create_table :users, force: true do |t| | |
t.string :name | |
end | |
end | |
class User < ActiveRecord::Base | |
end | |
class Exporter | |
attr_reader :scope | |
def initialize(scope) | |
@scope = scope | |
end | |
def each | |
return enum_for(__method__) unless block_given? | |
yield CSV.generate_line(["name"]) | |
scope.in_batches(of: 10).each_record do |user| | |
yield CSV.generate_line([user.name]) | |
end | |
end | |
end | |
class LockTest < Minitest::Test | |
def test_my_problem | |
User.insert_all(100.times.map { |i| {name: "User ##{i}"} }) | |
User.transaction do | |
# simulate something affecting data to be visible only in this transaction | |
sleep 2 | |
# and "streamed" export | |
enum = Exporter.new(User.all).each | |
# dummy example of consuming iterator | |
# in real code it is wrapped into IO-like API and consumed as IO using buffered read | |
# output is being zipped and uploaded in chunks to GCS | |
# | |
# for IO wrapper approach similar to https://github.com/lautis/piperator/blob/master/lib/piperator/io.rb is used | |
# | |
# iteration happens only inside of the transaction, enum is not leaked outside | |
File.open('/tmp/export.csv', 'w+') do |file| | |
loop do | |
data = enum.next | |
sleep 0.05 # simulate more work in the "streaming" chain | |
file.write(data) | |
rescue StopIteration | |
break | |
end | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment