Initial commit

This commit is contained in:
zhongjin
2020-06-15 10:58:47 +08:00
commit 4f1dfe7564
8590 changed files with 1516878 additions and 0 deletions
@@ -0,0 +1,131 @@
require 'yaml'
require 'resque'
require_relative '../../../../app/models/log'
require_relative '../../../../app/models/synchronization/member'
require_relative '../../../../lib/resque/synchronization_jobs'
require_relative '../../../../services/platform-limits/platform_limits'
require 'carto/configuration'
unless defined? Cartodb
config = Carto::Conf.new.app_config[ENV['RAILS_ENV'] || 'development']
Resque.redis = "#{config['redis']['host']}:#{config['redis']['port']}"
end
module CartoDB
module Synchronizer
class Collection
DEFAULT_RELATION = 'synchronizations'
def initialize(pg_options={}, relation=DEFAULT_RELATION)
@db = SequelRails.connection
@relation = relation
@records = []
end
def print_log(message, error=false)
puts message if error || ENV['VERBOSE']
end
def run
fetch
process
print_log 'Pass finished'
end
def fetch_all
query = db.fetch(%Q(
SELECT id FROM #{relation}
))
end
# Fetches and enqueues all syncs that should run
# @param force_all_syncs bool
def fetch_and_enqueue(force_all_syncs=false)
begin
if force_all_syncs
query = db.fetch(%Q(
SELECT r.name, r.id FROM #{relation} r, users u WHERE
(r.state = '#{CartoDB::Synchronization::Member::STATE_SUCCESS}'
OR r.state = '#{CartoDB::Synchronization::Member::STATE_SYNCING}')
AND u.id = user_id AND u.state = '#{Carto::User::STATE_ACTIVE}'
))
else
query = db.fetch(%Q(
SELECT r.name, r.id, r.user_id FROM #{relation} r, users u
WHERE EXTRACT(EPOCH FROM r.run_at) < #{Time.now.utc.to_f}
AND u.id = user_id AND u.state = '#{Carto::User::STATE_ACTIVE}'
AND
(
r.state = '#{CartoDB::Synchronization::Member::STATE_SUCCESS}'
OR (r.state = '#{CartoDB::Synchronization::Member::STATE_FAILURE}'
AND r.retried_times < #{CartoDB::Synchronization::Member::MAX_RETRIES})
)
))
end
success = true
rescue Exception => e
success = false
print_log("ERROR fetching sync tables: #{e.message}, #{e.backtrace}", true)
end
if success
print_log "Fetched #{query.count} records"
force_all_syncs ? enqueue_all(query) : enqueue_rate_limited(query)
end
self
end
# This is probably for testing purposes only, as fetch also does the processing
def process(members=@members)
print_log "Processing #{members.size} records"
members.each { |member|
print_log "Enqueueing #{member.name} (#{member.id})"
member.enqueue
}
end
attr_reader :records
private
def enqueue_all(query)
query.each { |record|
enqueue_record(record)
}
end
# @see /app/controllers/api/json/synchronizations_controller -> sync()
def enqueue_rate_limited(query)
query.each { |record|
user = Carto::User.where(id: record[:user_id]).first
next if user.nil?
platform_limit = CartoDB::PlatformLimits::Importer::UserConcurrentSyncsAmount.new({
user: user, redis: { db: $users_metadata }
})
if platform_limit.is_within_limit?
enqueue_record(record)
platform_limit.increment!
else
print_log "User '#{user.username}' hit concurrent syncs rate limit, '#{record[:name]}' skipped"
end
}
end
def enqueue_record(record_data)
print_log "Enqueueing '#{record_data[:name]}' (#{record_data[:id]})"
Resque.enqueue(Resque::SynchronizationJobs, job_id: record_data[:id])
db.run(%Q(
UPDATE #{relation} SET state = '#{CartoDB::Synchronization::Member::STATE_QUEUED}'
WHERE id = '#{record_data[:id]}'
))
end
attr_reader :db, :relation
attr_writer :records
end
end
end
@@ -0,0 +1,17 @@
module CartoDB
module Synchronizer
class Member
def initialize(attributes={})
@attributes = attributes
end
def run
puts "running"
self
end
attr_reader :attributes
end
end
end
@@ -0,0 +1,9 @@
{
"host" : "localhost",
"port" : 5432,
"user" : "postgres",
"password" : "",
"database" : "test",
"dbname" : "test"
}
@@ -0,0 +1,35 @@
require 'pg'
require 'sequel'
require 'json'
module CartoDB
module Synchronizer
module Factories
class PGConnection
def initialize
raise(
"Please configure your database settings " +
"in spec/factories/database.json"
) unless File.exists?(configuration_file)
@pg_options = ::JSON.parse(File.read(configuration_file))
end
def connection
Sequel.postgres(pg_options)
end
def pg_options
Hash[@pg_options.map { |k, v| [k.to_sym, v] }]
end
private
def configuration_file
File.join(File.dirname("#{__FILE__}"), 'database.json')
end
end
end
end
end
@@ -0,0 +1,71 @@
gem 'minitest'
require 'minitest/autorun'
require_relative '../../lib/synchronizer/collection'
require_relative '../factories/pg_connection'
require_relative '../../../data-repository/backend/sequel'
include CartoDB::Synchronizer
describe Collection do
before do
@stop = lambda { |result| EventMachine.stop }
@run_call = lambda { |records| records.process; EventMachine.stop }
db = Factories::PGConnection.new
@connection = db.connection
@options = db.pg_options
end
describe '#fetch' do
it 'fetchs all pending jobs from the synchronizations relation' do
repository =
DataRepository::Backend::Sequel.new(@connection, :synchronizations)
ensure_table_created_in(@connection)
id = repository.next_id.to_s
repository.store(id, synchronization(id))
collection = Collection.new(@options)
EventMachine.run { collection.fetch(@run_call, @stop) }
collection.members.to_a.length.must_equal ensure_table_created_in
collection.members.first.must_be_instance_of CartoDB::Synchronization::Member
end
end
describe '#process' do
it 'tells all members to run' do
member = Minitest::Mock.new
collection = Collection.new(@options)
member.expect(:enqueue, self)
collection.process([member])
member.verify
end
end
def synchronization(id)
{
id: id,
url: 'foo',
user_id: 1,
interval: 3600,
run_at: Time.now.utc - 2,
created_at: Time.now.utc,
updated_at: Time.now.utc
}
end
def fake_member
member = Object.new
def member.run; self; end
member
end
def ensure_table_created_in(connection)
end
end