diff --git a/.gitignore b/.gitignore index ae12a2a..020237a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,9 @@ Gemfile.lock *.log *.gem gems - -lib/message_store/event_store -lib/message_store/event_store.rb -lib/message_store/postgres -lib/message_store/postgres.rb +*scratch* +*notes* +loader.rb +/test/package/installed +/test/benchmark/tmp/**/* +_sketch.rb diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 0000000..e14e8ee --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,7 @@ +# Changes + +## 2.4.0.1 + +Thu Dec 19 2019 + +- Vestigial logging of correlation in Get::Stream is removed (It no longer has correlation handling as of v2) diff --git a/MIT-License.txt b/MIT-License.txt index cd0e498..b6856d0 100644 --- a/MIT-License.txt +++ b/MIT-License.txt @@ -1,4 +1,4 @@ -Copyright (c) 2016 Scott Bellware +Copyright (c) 2015-present Scott Bellware Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.md b/README.md index fe48846..85a13db 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# message_store +# message_store-postgres -Common primitives for platform-specific message store implementations. +Message store implementation for PostgreSQL. ## Documentation @@ -8,4 +8,4 @@ See the [Eventide documentation site](http://docs.eventide-project.org) for more ## License -The `event_stream-postgres` library is released under the [MIT License](https://github.com/eventide-project/event-stream-postgres/blob/master/MIT-License.txt). +The `message_store-postgres` library is released under the [MIT License](https://github.com/eventide-project/message-store-postgres/blob/master/MIT-License.txt). diff --git a/lib/message_store.rb b/lib/message_store.rb index bfdfcba..73ec386 100644 --- a/lib/message_store.rb +++ b/lib/message_store.rb @@ -1,5 +1,4 @@ -require 'pp' -require 'json' +require 'pg' require 'casing' require 'identifier/uuid' @@ -9,9 +8,12 @@ require 'virtual' require 'async_invocation' +require 'log' +require 'settings' + require 'message_store/expected_version' -require 'message_store/no_stream' require 'message_store/id' +require 'message_store/no_stream' require 'message_store/stream_name' require 'message_store/message_data' @@ -21,10 +23,20 @@ require 'message_store/log' +require 'message_store/settings' +require 'message_store/session' + +require 'message_store/put' +require 'message_store/write' + require 'message_store/get' require 'message_store/get/substitute' +require 'message_store/get/condition' +require 'message_store/get/stream' require 'message_store/get/stream/last' require 'message_store/get/stream/last/substitute' +require 'message_store/get/category' +require 'message_store/get/category/correlation' +require 'message_store/get/category/consumer_group' require 'message_store/read/iterator' require 'message_store/read' -require 'message_store/write' diff --git a/lib/message_store/controls.rb b/lib/message_store/controls.rb index 8d8d60a..69acb78 100644 --- a/lib/message_store/controls.rb +++ b/lib/message_store/controls.rb @@ -9,11 +9,11 @@ require 'message_store/controls/category' require 'message_store/controls/stream_name' require 'message_store/controls/read' +require 'message_store/controls/position' require 'message_store/controls/message_data' -require 'message_store/controls/message_data/hash' require 'message_store/controls/message_data/metadata' -require 'message_store/controls/message_data/write' +require 'message_store/controls/message_data/hash' require 'message_store/controls/message_data/read' -require 'message_store/controls/write' +require 'message_store/controls/message_data/write' +require 'message_store/controls/put' require 'message_store/controls/get' -require 'message_store/controls/get_last' diff --git a/lib/message_store/controls/get_last.rb b/lib/message_store/controls/get_last.rb deleted file mode 100644 index fddc074..0000000 --- a/lib/message_store/controls/get_last.rb +++ /dev/null @@ -1,33 +0,0 @@ -module MessageStore - module Controls - module GetLast - def self.example - Example.build - end - - def self.default_session - :default_session - end - - class Example - include MessageStore::Get::Stream::Last - - attr_writer :session - def session - @session ||= GetLast.default_session - end - - def call(stream_name) - end - - def configure(session: nil) - self.session = session - end - - def session?(session) - self.session.equal?(session) - end - end - end - end -end diff --git a/lib/message_store/controls/message_data/write.rb b/lib/message_store/controls/message_data/write.rb index ff45a38..5bb3c86 100644 --- a/lib/message_store/controls/message_data/write.rb +++ b/lib/message_store/controls/message_data/write.rb @@ -48,6 +48,26 @@ def self.data def self.metadata MessageData::Metadata.data end + + module List + Entry = Struct.new(:stream_name, :category, :message_data) + + def self.get(instances: nil, stream_name: nil, category: nil) + instances ||= 1 + + list = [] + instances.times do + instance_stream_name = stream_name || StreamName.example(category: category) + instance_category = MessageStore::StreamName.get_category(instance_stream_name) + + write_message = Controls::MessageData::Write.example + + list << Entry.new(instance_stream_name, instance_category, write_message) + end + + list + end + end end end end diff --git a/lib/message_store/controls/position.rb b/lib/message_store/controls/position.rb new file mode 100644 index 0000000..9ddde03 --- /dev/null +++ b/lib/message_store/controls/position.rb @@ -0,0 +1,13 @@ +module MessageStore + module Controls + module Position + def self.example + 1 + end + + def self.max + (2 ** 63) - 1 + end + end + end +end diff --git a/lib/message_store/controls/put.rb b/lib/message_store/controls/put.rb new file mode 100644 index 0000000..348f9a7 --- /dev/null +++ b/lib/message_store/controls/put.rb @@ -0,0 +1,26 @@ +module MessageStore + module Controls + module Put + def self.call(instances: nil, stream_name: nil, message_data: nil, message: nil, category: nil, type: nil) + instances ||= 1 + stream_name ||= StreamName.example(category: category) + message_data ||= message + + message_specified = !message_data.nil? + + message_data ||= MessageData::Write.example(type: type) + + position = nil + instances.times do + position = MessageStore::Put.(message_data, stream_name) + + unless message_specified + message_data.id = MessageData::Write.id + end + end + + [stream_name, position] + end + end + end +end diff --git a/lib/message_store/controls/read.rb b/lib/message_store/controls/read.rb index 769a8c6..855ad23 100644 --- a/lib/message_store/controls/read.rb +++ b/lib/message_store/controls/read.rb @@ -4,19 +4,10 @@ module Read def self.example(stream_name: nil) stream_name ||= StreamName.example - read = Example.build(stream_name) - - get = Get.example(stream_name: stream_name) - read.iterator.get = get + read = MessageStore::Read.build(stream_name) read end - - class Example - include MessageStore::Read - - def configure(*); end - end end end end diff --git a/lib/message_store/controls/write.rb b/lib/message_store/controls/write.rb deleted file mode 100644 index 50296bb..0000000 --- a/lib/message_store/controls/write.rb +++ /dev/null @@ -1,23 +0,0 @@ -module MessageStore - module Controls - module Write - def self.example - Example.build - end - - class Example - include MessageStore::Write - - def configure(*) - end - - def write(batch, stream_name, expected_version: nil) - logger.trace { "Writing batch (Stream Name: #{stream_name}, Number of Events: #{batch.length}, Expected Version: #{expected_version.inspect})" } - logger.debug { "Wrote batch (Stream Name: #{stream_name}, Number of Events: #{batch.length}, Expected Version: #{expected_version.inspect})" } - - nil - end - end - end - end -end diff --git a/lib/message_store/get.rb b/lib/message_store/get.rb index 54f93de..1169c7b 100644 --- a/lib/message_store/get.rb +++ b/lib/message_store/get.rb @@ -7,10 +7,157 @@ def self.included(cls) include Virtual include Log::Dependency - abstract :call + prepend BatchSize + + dependency :session, Session + abstract :stream_name - abstract :batch_size + abstract :sql_command + abstract :parameters + abstract :parameter_values abstract :last_position + abstract :log_text + + virtual :specialize_error + virtual :assure + end + end + + module BatchSize + def batch_size + @batch_size ||= Defaults.batch_size + end + end + + def self.build(stream_name, **args) + cls = specialization(stream_name) + cls.build(stream_name, **args) + end + + def self.configure(receiver, stream_name, **args) + attr_name = args.delete(:attr_name) + attr_name ||= :get + + instance = build(stream_name, **args) + receiver.public_send("#{attr_name}=", instance) + end + + ## Is there a path to removing this? - Aaron, Sun Jan 15 2023 + def configure(session: nil) + Session.configure(self, session: session) + end + + def self.call(stream_name, **args) + position = args.delete(:position) + instance = build(stream_name, **args) + instance.(position) + end + + def call(position=nil, stream_name: nil) + position ||= self.class::Defaults.position + + stream_name ||= self.stream_name + + assure + + logger.trace(tag: :get) { "Getting message data (#{log_text(stream_name, position)})" } + + result = get_result(stream_name, position) + + message_data = convert(result) + + logger.info(tag: :get) { "Finished getting message data (Count: #{message_data.length}, #{log_text(stream_name, position)})" } + logger.info(tags: [:data, :message_data]) { message_data.pretty_inspect } + + message_data + end + + def get_result(stream_name, position) + logger.trace(tag: :get) { "Getting result (#{log_text(stream_name, position)})" } + + parameter_values = parameter_values(stream_name, position) + + begin + result = session.execute(sql_command, parameter_values) + rescue PG::RaiseException => e + raise_error(e) + end + + logger.debug(tag: :get) { "Finished getting result (Count: #{result.ntuples}, #{log_text(stream_name, position)})" } + + result + end + + def convert(result) + logger.trace(tag: :get) { "Converting result to message data (Result Count: #{result.ntuples})" } + + message_data = result.map do |record| + Get.message_data(record) + end + + logger.debug(tag: :get) { "Converted result to message data (Message Data Count: #{message_data.length})" } + + message_data + end + + def self.message_data(record) + record['data'] = Get::Deserialize.data(record['data']) + record['metadata'] = Get::Deserialize.metadata(record['metadata']) + record['time'] = Get::Time.utc_coerced(record['time']) + + MessageData::Read.build(record) + end + + def raise_error(pg_error) + error_message = Get.error_message(pg_error) + + error = Condition.error(error_message) + + if error.nil? + error = specialize_error(error_message) + end + + if not error.nil? + logger.error { error_message } + raise error + end + + raise pg_error + end + + def self.error_message(pg_error) + pg_error.message.gsub('ERROR:', '').strip + end + + def self.specialization(stream_name) + if StreamName.category?(stream_name) + Category + else + Stream + end + end + + module Deserialize + def self.data(serialized_data) + return nil if serialized_data.nil? + Transform::Read.(serialized_data, :json, MessageData::Hash) + end + + def self.metadata(serialized_metadata) + return nil if serialized_metadata.nil? + Transform::Read.(serialized_metadata, :json, MessageData::Hash) + end + end + + module Time + def self.utc_coerced(local_time) + Clock::UTC.coerce(local_time) + end + end + + module Defaults + def self.batch_size + 1000 end end end diff --git a/lib/message_store/get/category.rb b/lib/message_store/get/category.rb new file mode 100644 index 0000000..7581bfe --- /dev/null +++ b/lib/message_store/get/category.rb @@ -0,0 +1,79 @@ +module MessageStore + module Get + class Category + Error = Class.new(RuntimeError) + + include Get + + initializer :category, na(:batch_size), :correlation, :consumer_group_member, :consumer_group_size, :condition + alias :stream_name :category + + def self.call(category, position: nil, batch_size: nil, correlation: nil, consumer_group_member: nil, consumer_group_size: nil, condition: nil, session: nil) + instance = build(category, batch_size: batch_size, correlation: correlation, consumer_group_member: consumer_group_member, consumer_group_size: consumer_group_size, condition: condition, session: session) + instance.(position) + end + + def self.build(category, batch_size: nil, correlation: nil, consumer_group_member: nil, consumer_group_size: nil, condition: nil, session: nil) + instance = new(category, batch_size, correlation, consumer_group_member, consumer_group_size, condition) + instance.configure(session: session) + instance + end + + def self.configure(receiver, category, attr_name: nil, batch_size: nil, correlation: nil, consumer_group_member: nil, consumer_group_size: nil, condition: nil, session: nil) + attr_name ||= :get + instance = build(category, batch_size: batch_size, correlation: correlation, consumer_group_member: consumer_group_member, consumer_group_size: consumer_group_size, condition: condition, session: session) + receiver.public_send("#{attr_name}=", instance) + end + + def sql_command + "SELECT * FROM get_category_messages(#{parameters});" + end + + def parameters + '$1::varchar, $2::bigint, $3::bigint, $4::varchar, $5::bigint, $6::bigint, $7::varchar' + end + + def parameter_values(category, position) + [ + category, + position, + batch_size, + correlation, + consumer_group_member, + consumer_group_size, + condition + ] + end + + def last_position(batch) + batch.last.global_position + end + + def specialize_error(error_message) + error = Correlation.error(error_message) + + if error.nil? + error = ConsumerGroup.error(error_message) + end + + error + end + + def log_text(category, position) + "Category: #{category}, Position: #{position.inspect}, Batch Size: #{batch_size.inspect}, Correlation: #{correlation.inspect}, Consumer Group Member: #{consumer_group_member.inspect}, Consumer Group Size: #{consumer_group_size.inspect}, Condition: #{condition.inspect})" + end + + def assure + if not MessageStore::StreamName.category?(category) + raise Error, "Must be a category (Stream Name: #{category})" + end + end + + module Defaults + def self.position + 1 + end + end + end + end +end diff --git a/lib/message_store/get/category/consumer_group.rb b/lib/message_store/get/category/consumer_group.rb new file mode 100644 index 0000000..6b2c4e3 --- /dev/null +++ b/lib/message_store/get/category/consumer_group.rb @@ -0,0 +1,18 @@ +module MessageStore + module Get + class Category + module ConsumerGroup + Error = Class.new(RuntimeError) + + def self.error(error_message) + if error_message.start_with?('Consumer group size must not be less than 1') || + error_message.start_with?('Consumer group member must be less than the group size') || + error_message.start_with?('Consumer group member must not be less than 0') || + error_message.start_with?('Consumer group member and size must be specified') + Error.new(error_message) + end + end + end + end + end +end diff --git a/lib/message_store/get/category/correlation.rb b/lib/message_store/get/category/correlation.rb new file mode 100644 index 0000000..5211b3a --- /dev/null +++ b/lib/message_store/get/category/correlation.rb @@ -0,0 +1,15 @@ +module MessageStore + module Get + class Category + module Correlation + Error = Class.new(RuntimeError) + + def self.error(error_message) + if error_message.start_with?('Correlation must be a category') + Error.new(error_message) + end + end + end + end + end +end diff --git a/lib/message_store/get/condition.rb b/lib/message_store/get/condition.rb new file mode 100644 index 0000000..466a5fe --- /dev/null +++ b/lib/message_store/get/condition.rb @@ -0,0 +1,13 @@ +module MessageStore + module Get + module Condition + Error = Class.new(RuntimeError) + + def self.error(error_message) + if error_message.start_with?('Retrieval with SQL condition is not activated') + Get::Condition::Error.new(error_message) + end + end + end + end +end diff --git a/lib/message_store/get/stream.rb b/lib/message_store/get/stream.rb new file mode 100644 index 0000000..a84f762 --- /dev/null +++ b/lib/message_store/get/stream.rb @@ -0,0 +1,65 @@ +module MessageStore + module Get + class Stream + Error = Class.new(RuntimeError) + + include Get + + initializer :stream_name, na(:batch_size), :condition + + def self.call(stream_name, position: nil, batch_size: nil, condition: nil, session: nil) + instance = build(stream_name, batch_size: batch_size, condition: condition, session: session) + instance.(position) + end + + def self.build(stream_name, batch_size: nil, condition: nil, session: nil) + instance = new(stream_name, batch_size, condition) + instance.configure(session: session) + instance + end + + def self.configure(receiver, stream_name, attr_name: nil, batch_size: nil, condition: nil, session: nil) + attr_name ||= :get + instance = build(stream_name, batch_size: batch_size, condition: condition, session: session) + receiver.public_send("#{attr_name}=", instance) + end + + def sql_command + "SELECT * FROM get_stream_messages(#{parameters});" + end + + def parameters + '$1::varchar, $2::bigint, $3::bigint, $4::varchar' + end + + def parameter_values(stream_name, position) + [ + stream_name, + position, + batch_size, + condition + ] + end + + def last_position(batch) + batch.last.position + end + + def log_text(stream_name, position) + "Stream Name: #{stream_name}, Position: #{position.inspect}, Batch Size: #{batch_size.inspect}, Condition: #{condition.inspect})" + end + + def assure + if MessageStore::StreamName.category?(stream_name) + raise Error, "Must be a stream name (Category: #{stream_name})" + end + end + + module Defaults + def self.position + 0 + end + end + end + end +end diff --git a/lib/message_store/get/stream/last.rb b/lib/message_store/get/stream/last.rb index 1d16c9d..6717b0a 100644 --- a/lib/message_store/get/stream/last.rb +++ b/lib/message_store/get/stream/last.rb @@ -1,62 +1,102 @@ module MessageStore module Get - module Stream - module Last - def self.included(cls) - cls.class_exec do - include Dependency - include Virtual - include Log::Dependency + class Stream + class Last + include Dependency + include Virtual + include Log::Dependency - extend Build - extend Call - extend Configure + dependency :session, Session - prepend InstanceActuator + def self.build(session: nil) + instance = new + Session.configure(instance, session: session) + instance + end - virtual :configure - abstract :call + def self.call(stream_name, type=nil, session: nil) + instance = build(session: session) + instance.(stream_name, type) + end - const_set :Substitute, Substitute - end + def self.configure(receiver, session: nil, attr_name: nil) + attr_name ||= :get_last + + instance = build(session: session) + receiver.public_send("#{attr_name}=", instance) + instance end - module InstanceActuator - def call(stream_name, type=nil) - logger.trace(tag: :get) { "Getting last message data (Stream Name: #{stream_name})" } + def call(stream_name, type=nil) + logger.trace(tag: :get) { "Getting last message data (Stream Name: #{stream_name})" } - message_data = super + result = get_result(stream_name, type) - logger.info(tag: :get) { "Finished getting message data (Stream Name: #{stream_name})" } - logger.info(tags: [:data, :message_data]) { message_data.pretty_inspect } + return nil if result.nil? - message_data - end + message_data = convert(result[0]) + + logger.info(tag: :get) { "Finished getting message data (Stream Name: #{stream_name})" } + logger.info(tags: [:data, :message_data]) { message_data.pretty_inspect } + + message_data end - module Build - def build(session: nil) - instance = new - instance.configure(session: session) - instance - end + def get_result(stream_name, type) + logger.trace(tag: :get) { "Getting last record (Stream: #{stream_name})" } + + parameter_values = parameter_values(stream_name, type) + sql_command = sql_command(type) + + result = session.execute(sql_command, parameter_values) + + logger.debug(tag: :get) { "Finished getting result (Count: #{result.ntuples}, Stream: #{stream_name}" } + + return nil if result.ntuples == 0 + + result + end + + def sql_command(type) + parameters = parameters(type) + + "SELECT * FROM get_last_stream_message(#{parameters});" end - module Configure - def configure(receiver, session: nil, attr_name: nil) - attr_name ||= :get_last + def parameters(type) + parameters = "$1::varchar" - instance = build(session: session) - receiver.public_send("#{attr_name}=", instance) - instance + # Backwards compatibility with versions of message-db that do not + # support the type parameter - Aaron, Scott, Tue Jul 12 2022 + if not type.nil? + parameters << ", $2::varchar" end + + parameters end - module Call - def call(stream_name, type=nil, session: nil) - instance = build(session: session) - instance.(stream_name, type) + def parameter_values(stream_name, type) + parameter_values = [ + stream_name + ] + + # Backwards compatibility with versions of message-db that do not + # support the type parameter - Aaron, Scott, Tue Jul 12 2022 + if not type.nil? + parameter_values << type end + + parameter_values + end + + def convert(record) + logger.trace(tag: :get) { "Converting record to message data" } + + message_data = Get.message_data(record) + + logger.debug(tag: :get) { "Converted record to message data" } + + message_data end end end diff --git a/lib/message_store/get/stream/last/substitute.rb b/lib/message_store/get/stream/last/substitute.rb index b1e4de2..2627dfd 100644 --- a/lib/message_store/get/stream/last/substitute.rb +++ b/lib/message_store/get/stream/last/substitute.rb @@ -1,15 +1,13 @@ module MessageStore module Get - module Stream - module Last + class Stream + class Last module Substitute def self.build GetLast.new end - class GetLast - include Get::Stream::Last - + class GetLast < Stream::Last def call(stream_name, type=nil) streams[stream_name] end diff --git a/lib/message_store/get/substitute.rb b/lib/message_store/get/substitute.rb index 6fec04e..34d9f99 100644 --- a/lib/message_store/get/substitute.rb +++ b/lib/message_store/get/substitute.rb @@ -1,3 +1,4 @@ +## Add tests, this did not have tests in message-store either - Aaron, Sun Jan 15 2023 module MessageStore module Get class Substitute diff --git a/lib/message_store/put.rb b/lib/message_store/put.rb new file mode 100644 index 0000000..e42acf9 --- /dev/null +++ b/lib/message_store/put.rb @@ -0,0 +1,140 @@ +module MessageStore + class Put + include Dependency + include Log::Dependency + + dependency :session, Session + dependency :identifier, Identifier::UUID::Random + + def self.build(session: nil) + instance = new + Session.configure(instance, session: session) + Identifier::UUID::Random.configure(instance) + instance + end + + def self.configure(receiver, session: nil, attr_name: nil) + attr_name ||= :put + instance = build(session: session) + receiver.public_send "#{attr_name}=", instance + end + + def self.call(write_message, stream_name, expected_version: nil, session: nil) + instance = build(session: session) + instance.(write_message, stream_name, expected_version: expected_version) + end + + def call(write_message, stream_name, expected_version: nil) + logger.trace(tag: :put) { "Putting message data (Type: #{write_message.type}, Stream Name: #{stream_name}, Expected Version: #{expected_version.inspect})" } + logger.trace(tags: [:data, :message_data]) { write_message.pretty_inspect } + + write_message.id ||= identifier.get + + id, type, data, metadata = destructure_message(write_message) + expected_version = ExpectedVersion.canonize(expected_version) + + insert_message(id, stream_name, type, data, metadata, expected_version).tap do |position| + logger.info(tag: :put) { "Put message data (Type: #{write_message.type}, Stream Name: #{stream_name}, Expected Version: #{expected_version.inspect}, ID: #{id.inspect}, Position: #{position})" } + logger.info(tags: [:data, :message_data]) { write_message.pretty_inspect } + end + end + + def destructure_message(write_message) + id = write_message.id + type = write_message.type + data = write_message.data + metadata = write_message.metadata + + logger.debug(tags: [:data, :message_data]) { "ID: #{id.pretty_inspect}" } + logger.debug(tags: [:data, :message_data]) { "Type: #{type.pretty_inspect}" } + logger.debug(tags: [:data, :message_data]) { "Data: #{data.pretty_inspect}" } + logger.debug(tags: [:data, :message_data]) { "Metadata: #{metadata.pretty_inspect}" } + + return id, type, data, metadata + end + + def insert_message(id, stream_name, type, data, metadata, expected_version) + transformed_data = transformed_data(data) + transformed_metadata = transformed_metadata(metadata) + records = execute_query(id, stream_name, type, transformed_data, transformed_metadata, expected_version) + position(records) + end + + def execute_query(id, stream_name, type, transformed_data, transformed_metadata, expected_version) + logger.trace(tag: :put) { "Executing insert (Stream Name: #{stream_name}, Type: #{type}, Expected Version: #{expected_version.inspect}, ID: #{id.inspect})" } + + params = [ + id, + stream_name, + type, + transformed_data, + transformed_metadata, + expected_version + ] + + begin + records = session.execute(self.class.statement, params) + rescue PG::RaiseException => e + raise_error e + end + + logger.debug(tag: :put) { "Executed insert (Type: #{type}, Stream Name: #{stream_name}, Expected Version: #{expected_version.inspect}, ID: #{id.inspect})" } + + records + end + + def self.statement + @statement ||= "SELECT write_message($1::varchar, $2::varchar, $3::varchar, $4::jsonb, $5::jsonb, $6::bigint);" + end + + def transformed_data(data) + transformed_data = nil + + if data.is_a?(Hash) && data.empty? + data = nil + end + + unless data.nil? + transformable_data = MessageData::Hash[data] + transformed_data = Transform::Write.(transformable_data, :json) + end + + logger.debug(tags: [:data, :serialize]) { "Transformed Data: #{transformed_data.inspect}" } + transformed_data + end + + def transformed_metadata(metadata) + transformed_metadata = nil + + if metadata.is_a?(Hash) && metadata.empty? + metadata = nil + end + + unless metadata.nil? + transformable_metadata = MessageData::Hash[metadata] + transformed_metadata = Transform::Write.(transformable_metadata, :json) + end + + logger.debug(tags: [:data, :serialize]) { "Transformed Metadata: #{transformed_metadata.inspect}" } + transformed_metadata + end + + def position(records) + position = nil + unless records[0].nil? + position = records[0].values[0] + end + position + end + + def raise_error(pg_error) + error_message = pg_error.message + if error_message.include? 'Wrong expected version' + error_message.gsub!('ERROR:', '').strip! + logger.error { error_message } + raise ExpectedVersion::Error, error_message + end + raise pg_error + end + end +end diff --git a/lib/message_store/read.rb b/lib/message_store/read.rb index bf3a501..e32b633 100644 --- a/lib/message_store/read.rb +++ b/lib/message_store/read.rb @@ -1,48 +1,36 @@ module MessageStore - module Read - def self.included(cls) - cls.class_exec do - include Dependency - include Initializer - include Virtual - include Log::Dependency + class Read + include Dependency + include Initializer + include Virtual + include Log::Dependency - extend Build - extend Call - extend Configure + Error = Class.new(RuntimeError) - dependency :iterator, Iterator + dependency :iterator, Iterator - initializer :stream_name, :position, :batch_size + initializer :stream_name, :position, :batch_size - abstract :configure - end - end + def self.build(stream_name, position: nil, batch_size: nil, session: nil, condition: nil, **arguments) + instance = new(stream_name, position, batch_size) - Error = Class.new(RuntimeError) + Iterator.configure(instance, position) - module Build - def build(stream_name, position: nil, batch_size: nil, session: nil, **arguments) - new(stream_name, position, batch_size).tap do |instance| - Iterator.configure(instance, position) - instance.configure(session: session, **arguments) - end - end + iterator = instance.iterator + Get.configure(iterator, stream_name, batch_size: batch_size, condition: condition, session: session) + + instance end - module Call - def call(stream_name, position: nil, batch_size: nil, session: nil, **arguments, &action) - instance = build(stream_name, position: position, batch_size: batch_size, session: session, **arguments) - instance.(&action) - end + def self.call(stream_name, position: nil, batch_size: nil, session: nil, **arguments, &action) + instance = build(stream_name, position: position, batch_size: batch_size, session: session, **arguments) + instance.(&action) end - module Configure - def configure(receiver, stream_name, attr_name: nil, position: nil, batch_size: nil, session: nil, **arguments) - attr_name ||= :read - instance = build(stream_name, position: position, batch_size: batch_size, session: session, **arguments) - receiver.public_send "#{attr_name}=", instance - end + def self.configure(receiver, stream_name, attr_name: nil, position: nil, batch_size: nil, session: nil, **arguments) + attr_name ||= :read + instance = build(stream_name, position: position, batch_size: batch_size, session: session, **arguments) + receiver.public_send "#{attr_name}=", instance end def call(&action) @@ -77,5 +65,11 @@ def enumerate_message_data(&action) logger.debug(tag: :read) { "Enumerated (Stream Name: #{stream_name})" } end + + module Defaults + def self.batch_size + Get::Defaults.batch_size + end + end end end diff --git a/lib/message_store/read/iterator.rb b/lib/message_store/read/iterator.rb index 22e3019..1774250 100644 --- a/lib/message_store/read/iterator.rb +++ b/lib/message_store/read/iterator.rb @@ -1,5 +1,6 @@ +## Add tests - Aaron, Sun Jan 15 2023 module MessageStore - module Read + class Read class Iterator include Dependency include Initializer @@ -25,10 +26,10 @@ def batch_size end def self.build(position=nil) - new.tap do |instance| - instance.starting_position = position - Log.get(self).debug { "Built Iterator (Starting Position: #{position.inspect})" } - end + instance = new + instance.starting_position = position + Log.get(self).debug { "Built Iterator (Starting Position: #{position.inspect})" } + instance end def self.configure(receiver, position=nil, attr_name: nil) diff --git a/lib/message_store/session.rb b/lib/message_store/session.rb new file mode 100644 index 0000000..c58fbff --- /dev/null +++ b/lib/message_store/session.rb @@ -0,0 +1,174 @@ +module MessageStore + class Session + Error = Class.new(RuntimeError) + + include Dependency + include Settings::Setting + + include Log::Dependency + + dependency :clock, Clock::UTC + + def self.settings + Settings.names + end + + settings.each do |s| + setting s + end + + attr_accessor :connection + attr_accessor :executed_time + + def self.build(settings: nil) + instance = new + + settings ||= Settings.instance + settings.set(instance) + + Clock::UTC.configure(instance) + + instance + end + + def self.configure(receiver, session: nil, settings: nil, attr_name: nil) + attr_name ||= :session + + if session != nil && settings != nil + error_msg = "Session configured with both settings and session arguments. Use one or the other, but not both." + logger.error(tag: :session) { error_msg } + raise Error, error_msg + end + + instance = session || build(settings: settings) + receiver.public_send "#{attr_name}=", instance + instance + end + + def open + logger.trace(tag: :session) { "Connecting to database" } + + if connected? + logger.debug(tag: :session) { "Already connected. A new connection will not be built." } + return connection + end + + logger.debug(tag: :session) { "Not connected. A new connection will be built." } + connection = self.class.build_connection(self) + self.connection = connection + + logger.debug(tag: :session) { "Connected to database" } + + connection + end + alias :connect :open + + def self.build_connection(instance) + settings = instance.settings + logger.trace(tag: :session) { "Building new connection to database (Settings: #{LogText.settings(settings).inspect})" } + + connection = PG::Connection.open(settings) + connection.type_map_for_results = PG::BasicTypeMapForResults.new(connection) + + logger.debug(tag: :session) { "Built new connection to database (Settings: #{LogText.settings(settings).inspect})" } + + connection + end + + def connected? + return false if connection.nil? + + status = PG::CONNECTION_OK + begin + status = connection.status + rescue PG::ConnectionBad + status = nil + end + + status == PG::CONNECTION_OK + end + alias :open? :connected? + + def close + if connection.nil? + return + end + + connection.close + self.connection = nil + end + + def reset + connection.reset + end + + def execute(sql_command, params=nil) + logger.trace(tag: :session) { "Executing SQL command" } + logger.trace(tag: :sql) { sql_command } + logger.trace(tag: :data) { params.pretty_inspect } + + unless connected? + connect + end + + if params.nil? + connection.exec(sql_command).tap do + self.executed_time = clock.now + logger.debug(tag: :session) { "Executed SQL command (no params)" } + end + else + connection.exec_params(sql_command, params).tap do + self.executed_time = clock.now + logger.debug(tag: :session) { "Executed SQL command with params" } + end + end + end + + def executed_time_elapsed_milliseconds + return nil if executed_time.nil? + + (clock.now - executed_time) * 1000 + end + + def transaction(&blk) + unless connected? + connect + end + + connection.transaction(&blk) + end + + def escape(data) + connection = connect + + escaped_data = connection.escape(data) + + escaped_data + end + + def settings + settings = {} + self.class.settings.each do |s| + val = public_send(s) + settings[s] = val unless val.nil? + end + settings + end + + def self.logger + @logger ||= Log.get self + end + + module LogText + def self.settings(settings) + s = settings.dup + + if s.has_key?(:password) + s[:password] = '*' * 8 + end + + s + end + end + end +end diff --git a/lib/message_store/settings.rb b/lib/message_store/settings.rb new file mode 100644 index 0000000..095ef45 --- /dev/null +++ b/lib/message_store/settings.rb @@ -0,0 +1,39 @@ +module MessageStore + class Settings < ::Settings + def self.instance + @instance ||= build + end + + def self.data_source + Defaults.data_source + end + + def self.names + [ + :dbname, + :host, + :hostaddr, + :port, + :user, + :password, + :connect_timeout, + :options, + :sslmode, + :krbsrvname, + :gsslib, + :service, + :keepalives, + :keepalives_idle, + :keepalives_interval, + :keepalives_count, + :tcp_user_timeout + ] + end + + class Defaults + def self.data_source + ENV['MESSAGE_STORE_SETTINGS_PATH'] || 'settings/message_store_postgres.json' + end + end + end +end diff --git a/lib/message_store/write.rb b/lib/message_store/write.rb index b5c1655..f407ed3 100644 --- a/lib/message_store/write.rb +++ b/lib/message_store/write.rb @@ -1,44 +1,28 @@ module MessageStore - module Write - def self.included(cls) - cls.class_exec do - include Dependency - include Virtual - include Log::Dependency + class Write + include Dependency + include Virtual + include Log::Dependency - extend Build - extend Call - extend Configure + dependency :identifier, Identifier::UUID::Random + dependency :put - dependency :identifier, Identifier::UUID::Random - - abstract :configure - abstract :write - end + def self.build(session: nil) + instance = new + Identifier::UUID::Random.configure(instance) + Put.configure(instance, session: session) + instance end - module Build - def build(session: nil) - instance = new - Identifier::UUID::Random.configure(instance) - instance.configure(session: session) - instance - end + def self.configure(receiver, session: nil, attr_name: nil) + attr_name ||= :write + instance = build(session: session) + receiver.public_send "#{attr_name}=", instance end - module Configure - def configure(receiver, session: nil, attr_name: nil) - attr_name ||= :write - instance = build(session: session) - receiver.public_send "#{attr_name}=", instance - end - end - - module Call - def call(message_data, stream_name, expected_version: nil, session: nil) - instance = build(session: session) - instance.(message_data, stream_name, expected_version: expected_version) - end + def self.call(message_data, stream_name, expected_version: nil, session: nil) + instance = build(session: session) + instance.(message_data, stream_name, expected_version: expected_version) end def call(message_data, stream_name, expected_version: nil) @@ -62,7 +46,45 @@ def call(message_data, stream_name, expected_version: nil) position end - alias :write :call + + def write(batch, stream_name, expected_version: nil) + logger.trace(tag: :write) do + message_types = batch.map {|message_data| message_data.type }.uniq.join(', ') + "Writing batch (Stream Name: #{stream_name}, Types: #{message_types}, Number of Messages: #{batch.length}, Expected Version: #{expected_version.inspect})" + end + + unless expected_version.nil? + expected_version = ExpectedVersion.canonize(expected_version) + end + + last_position = nil + put.session.transaction do + batch.each do |message_data| + last_position = write_message_data(message_data, stream_name, expected_version: expected_version) + + unless expected_version.nil? + expected_version += 1 + end + end + end + + logger.debug(tag: :write) do + message_types = batch.map {|message_data| message_data.type }.uniq.join(', ') + "Wrote batch (Stream Name: #{stream_name}, Types: #{message_types}, Number of Messages: #{batch.length}, Expected Version: #{expected_version.inspect})" + end + + last_position + end + + def write_message_data(message_data, stream_name, expected_version: nil) + logger.trace(tag: :write) { "Writing message data (Stream Name: #{stream_name}, Type: #{message_data.type}, Expected Version: #{expected_version.inspect})" } + logger.trace(tags: [:data, :message_data]) { message_data.pretty_inspect } + + put.(message_data, stream_name, expected_version: expected_version).tap do + logger.debug(tag: :write) { "Wrote message data (Stream Name: #{stream_name}, Type: #{message_data.type}, Expected Version: #{expected_version.inspect})" } + logger.debug(tags: [:data, :message_data]) { message_data.pretty_inspect } + end + end def set_ids(batch) batch.each do |message_data| diff --git a/message_store.gemspec b/message_store.gemspec index 0646371..09175a5 100644 --- a/message_store.gemspec +++ b/message_store.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |s| s.name = 'evt-message_store' s.version = '2.4.0.0' - s.summary = 'Common primitives for platform-specific message store implementations' + s.summary = 'Message store implementation for PostgreSQL' s.description = ' ' s.authors = ['The Eventide Project'] @@ -11,17 +11,26 @@ Gem::Specification.new do |s| s.licenses = ['MIT'] s.require_paths = ['lib'] - s.files = Dir.glob('{lib}/**/*') + s.files = Dir.glob('{lib,database}/**/*') s.platform = Gem::Platform::RUBY s.required_ruby_version = '>= 2.4.0' - s.add_runtime_dependency 'evt-casing' - s.add_runtime_dependency 'evt-schema' + s.executables = Dir.glob('scripts/evt-*').map(&File.method(:basename)) + s.bindir = 'scripts' + + s.add_runtime_dependency 'evt-log' + s.add_runtime_dependency 'evt-settings' s.add_runtime_dependency 'evt-initializer' s.add_runtime_dependency 'evt-identifier-uuid' s.add_runtime_dependency 'evt-transform' s.add_runtime_dependency 'evt-virtual' + s.add_runtime_dependency 'evt-casing' s.add_runtime_dependency 'evt-async_invocation' + s.add_runtime_dependency 'message-db' + s.add_runtime_dependency 'pg' + s.add_development_dependency 'test_bench' + s.add_development_dependency 'evt-diagnostics-sample' + s.add_development_dependency 'ntl-actor' end diff --git a/settings/message_store_postgres.json b/settings/message_store_postgres.json new file mode 100644 index 0000000..2c62f9a --- /dev/null +++ b/settings/message_store_postgres.json @@ -0,0 +1,19 @@ +{ + "dbname": "message_store", + "host": "localhost", + "hostaddr": "127.0.0.1", + "port": 5432, + "user": "message_store", + "password": null, + "connect_timeout": null, + "options": null, + "sslmode": null, + "krbsrvname": null, + "gsslib": null, + "service": null, + "keepalives": null, + "keepalives_idle": null, + "keepalives_interval": null, + "keepalives_count": null, + "tcp_user_timeout": null +} diff --git a/test/automated/get/category/generalized/get_messages.rb b/test/automated/get/category/generalized/get_messages.rb new file mode 100644 index 0000000..9e08b35 --- /dev/null +++ b/test/automated/get/category/generalized/get_messages.rb @@ -0,0 +1,32 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Category" do + context "Generalized" do + context "Get Messages" do + category = Controls::Category.example + + Controls::Put.(category: category) + Controls::Put.(category: category) + + message_data = Get.(category) + + context "Messages Retrieved" do + test "Messages from the all streams in the category" do + assert(message_data.length == 2) + end + + context "Message category is the category written" do + message_data.each do |md| + message_cateogry = StreamName.get_category(md.stream_name) + + test do + assert(message_cateogry == category) + end + end + end + end + end + end + end +end diff --git a/test/automated/get/category/generalized/specialization.rb b/test/automated/get/category/generalized/specialization.rb new file mode 100644 index 0000000..7c37ea4 --- /dev/null +++ b/test/automated/get/category/generalized/specialization.rb @@ -0,0 +1,55 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Category" do + context "Generalized" do + context "Specialization" do + category = Controls::Category.example + batch_size = 1 + correlation = 'someCorrelation' + consumer_group_member = 0 + consumer_group_size = 1 + condition = 'global_position >= 1' + session = Session.build + + get = Get.build(category, batch_size: batch_size, correlation: correlation, consumer_group_member: consumer_group_member, consumer_group_size: consumer_group_size, condition: condition, session: session) + + context "Type" do + test "Get::Category" do + assert(get.instance_of?(Get::Category)) + end + end + + context "Attributes" do + test "category" do + assert(get.category == category) + end + + test "batch_size" do + assert(get.batch_size == batch_size) + end + + test "correlation" do + assert(get.correlation == correlation) + end + + test "consumer_group_member" do + assert(get.consumer_group_member == consumer_group_member) + end + + test "consumer_group_size" do + assert(get.consumer_group_size == consumer_group_size) + end + + test "condition" do + assert(get.condition == condition) + end + + test "session" do + assert(get.session == session) + end + end + end + end + end +end diff --git a/test/automated/get/category/specialized/batch_size.rb b/test/automated/get/category/specialized/batch_size.rb new file mode 100644 index 0000000..2d4de98 --- /dev/null +++ b/test/automated/get/category/specialized/batch_size.rb @@ -0,0 +1,23 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Category" do + context "Specialized" do + context "Batch Size" do + category = Controls::Category.example + + Controls::Put.(category: category) + Controls::Put.(category: category) + Controls::Put.(category: category) + + messages = Get::Category.(category, batch_size: 2) + + number_of_messages = messages.length + + test "Number of messages retrieved is the specified batch size" do + assert(number_of_messages == 2) + end + end + end + end +end diff --git a/test/automated/get/category/specialized/condition/condition.rb b/test/automated/get/category/specialized/condition/condition.rb new file mode 100644 index 0000000..7335174 --- /dev/null +++ b/test/automated/get/category/specialized/condition/condition.rb @@ -0,0 +1,30 @@ +require_relative '../../../../automated_init' + +context "Get" do + context "Category" do + context "Specialized" do + context "Condition" do + category = Controls::Category.example + + stream_name, _ = Controls::Put.(instances: 3, category: category) + + condition = 'position = 0 OR position = 2' + + settings = MessageStore::Settings.build + session = Session.new + settings.set(session) + session.options = '-c message_store.sql_condition=on' + + messages = Get::Category.(category, batch_size: 3, condition: condition, session: session) + + message_positions = messages.map do |message| + message.position + end + + test "Retrieves messages that meet the condition" do + assert(message_positions == [0, 2]) + end + end + end + end +end diff --git a/test/automated/get/category/specialized/condition/not_activated_error.rb b/test/automated/get/category/specialized/condition/not_activated_error.rb new file mode 100644 index 0000000..1e6127a --- /dev/null +++ b/test/automated/get/category/specialized/condition/not_activated_error.rb @@ -0,0 +1,26 @@ +require_relative '../../../../automated_init' + +context "Get" do + context "Category" do + context "Specialized" do + context "Condition" do + context "Condition Is Not Activated" do + category = Controls::Category.example + + condition = 'some condition' + + settings = MessageStore::Settings.build + session = Session.new + settings.set(session) + session.options = nil + + test "Is an error" do + assert_raises(Get::Condition::Error) do + Get::Category.(category, batch_size: 3, condition: condition, session: session) + end + end + end + end + end + end +end diff --git a/test/automated/get/category/specialized/consumer_groups/consumer_groups.rb b/test/automated/get/category/specialized/consumer_groups/consumer_groups.rb new file mode 100644 index 0000000..2d80f59 --- /dev/null +++ b/test/automated/get/category/specialized/consumer_groups/consumer_groups.rb @@ -0,0 +1,41 @@ +require_relative '../../../../automated_init' + +context "Get" do + context "Category" do + context "Specialized" do + context "Consumer Groups" do + category = Controls::Category.example + + instances = 11 + partitions = 3 + + instances.times do + Controls::Put.(category: category) + end + + retrieved_message_data = [] + partitions.times do |i| + retrieved_message_data += Get::Category.(category, consumer_group_member: i, consumer_group_size: partitions) + end + + context "Message Partitions" do + context "Distribution" do + retrieved_count = retrieved_message_data.count + + test "Across partitions" do + assert(retrieved_count == instances) + end + end + end + + context "Messages in all partitions" do + test "Are of the same category" do + retrieved_message_data.each do |message_data| + assert(StreamName.get_category(message_data.stream_name) == category) + end + end + end + end + end + end +end diff --git a/test/automated/get/category/specialized/consumer_groups/error.rb b/test/automated/get/category/specialized/consumer_groups/error.rb new file mode 100644 index 0000000..5e15252 --- /dev/null +++ b/test/automated/get/category/specialized/consumer_groups/error.rb @@ -0,0 +1,51 @@ +require_relative '../../../../automated_init' + +context "Get" do + context "Category" do + context "Specialized" do + context "Consumer Groups" do + context "Error" do + context "Consumer Group Size Is Less than 1" do + test "Is an error" do + assert_raises(MessageStore::Get::Category::ConsumerGroup::Error) do + Get::Category.('someCategory', consumer_group_member: 0, consumer_group_size: 0) + end + end + end + + context "Consumer Group Member Is Greater than the Consumer Group Size" do + test "Is an error" do + assert_raises(MessageStore::Get::Category::ConsumerGroup::Error) do + Get::Category.('someCategory', consumer_group_member: 2, consumer_group_size: 1) + end + end + end + + context "Consumer Group Member Is Less than 0" do + test "Is an error" do + assert_raises(MessageStore::Get::Category::ConsumerGroup::Error) do + Get::Category.('someCategory', consumer_group_member: -1, consumer_group_size: 1) + end + end + end + + context "Consumer Group Size is Missing" do + test "Is an error" do + assert_raises(MessageStore::Get::Category::ConsumerGroup::Error) do + Get::Category.('someCategory', consumer_group_member: 0) + end + end + end + + context "Consumer Group Member is Missing" do + test "Is an error" do + assert_raises(MessageStore::Get::Category::ConsumerGroup::Error) do + Get::Category.('someCategory', consumer_group_size: 1) + end + end + end + end + end + end + end +end diff --git a/test/automated/get/category/specialized/correlation/correlation.rb b/test/automated/get/category/specialized/correlation/correlation.rb new file mode 100644 index 0000000..69d131a --- /dev/null +++ b/test/automated/get/category/specialized/correlation/correlation.rb @@ -0,0 +1,44 @@ +require_relative '../../../../automated_init' + +context "Get" do + context "Category" do + context "Specialized" do + context "Correlation" do + correlation_category = Controls::Category.example + + correlation_stream_name = Controls::StreamName.example(category: correlation_category) + + correlation_metadata = { + correlation_stream_name: correlation_stream_name + } + + category = Controls::Category.example + + message_data = Controls::MessageData::Write.example + + message_data.metadata = { + correlation_stream_name: SecureRandom.hex + } + + stream_name = Controls::StreamName.example(category: category) + Put.(message_data, stream_name) + + 2.times do + message_data = Controls::MessageData::Write.example(metadata: correlation_metadata) + stream_name = Controls::StreamName.example(category: category) + Put.(message_data, stream_name) + end + + message_datas = Get::Category.(category, correlation: correlation_category) + + correlation_stream_names = message_datas.map do |message_data| + message_data.metadata[:correlation_stream_name] + end + + test "Retrieves messages that meet the condition" do + assert(correlation_stream_names == [correlation_stream_name, correlation_stream_name]) + end + end + end + end +end diff --git a/test/automated/get/category/specialized/correlation/error.rb b/test/automated/get/category/specialized/correlation/error.rb new file mode 100644 index 0000000..200f764 --- /dev/null +++ b/test/automated/get/category/specialized/correlation/error.rb @@ -0,0 +1,21 @@ +require_relative '../../../../automated_init' + +context "Get" do + context "Category" do + context "Specialized" do + context "Correlation" do + context "Not a Category" do + correlation = Controls::StreamName.example + + category = Controls::Category.example + + test "Is an error" do + assert_raises(MessageStore::Get::Category::Correlation::Error) do + Get::Category.(category, correlation: correlation) + end + end + end + end + end + end +end diff --git a/test/automated/get/category/specialized/dependency.rb b/test/automated/get/category/specialized/dependency.rb new file mode 100644 index 0000000..5b7170e --- /dev/null +++ b/test/automated/get/category/specialized/dependency.rb @@ -0,0 +1,38 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Category" do + context "Specialized" do + context "Dependency" do + category = Controls::Category.example + + Controls::Put.(category: category) + Controls::Put.(category: category) + + receiver = OpenStruct.new + + Get::Category.configure(receiver, category) + + get = receiver.get + + message_data = get.() + + context "Messages Retrieved" do + test "Messages from the all streams in the category" do + assert(message_data.length == 2) + end + + context "Message category is the category written" do + message_data.each do |md| + message_cateogry = StreamName.get_category(md.stream_name) + + test do + assert(message_cateogry == category) + end + end + end + end + end + end + end +end diff --git a/test/automated/get/category/specialized/get_messages.rb b/test/automated/get/category/specialized/get_messages.rb new file mode 100644 index 0000000..a353950 --- /dev/null +++ b/test/automated/get/category/specialized/get_messages.rb @@ -0,0 +1,32 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Category" do + context "Specialized" do + context "Get Messages" do + category = Controls::Category.example + + Controls::Put.(category: category) + Controls::Put.(category: category) + + message_data = Get::Category.(category) + + context "Messages Retrieved" do + test "Messages from the all streams in the category" do + assert(message_data.length == 2) + end + + context "Message category is the category written" do + message_data.each do |md| + message_cateogry = StreamName.get_category(md.stream_name) + + test do + assert(message_cateogry == category) + end + end + end + end + end + end + end +end diff --git a/test/automated/get/category/specialized/no_messages.rb b/test/automated/get/category/specialized/no_messages.rb new file mode 100644 index 0000000..422dabd --- /dev/null +++ b/test/automated/get/category/specialized/no_messages.rb @@ -0,0 +1,17 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Category" do + context "Specialized" do + context "No Messages" do + category = Controls::Category.example + + batch = Get::Category.(category) + + test "Retrieves no messages" do + assert(batch == []) + end + end + end + end +end diff --git a/test/automated/get/category/specialized/position.rb b/test/automated/get/category/specialized/position.rb new file mode 100644 index 0000000..eaec3eb --- /dev/null +++ b/test/automated/get/category/specialized/position.rb @@ -0,0 +1,20 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Category" do + context "Specialized" do + context "Position" do + category = Controls::Category.example + stream_name = Controls::StreamName.example(category: category) + + Controls::Put.(instances: 2, stream_name: stream_name) + + batch = Get::Category.(category, position: 1, batch_size: 1) + + test "Retrieves messages from the starting position" do + assert(batch.length == 1) + end + end + end + end +end diff --git a/test/automated/get/category/specialized/stream_name_error.rb b/test/automated/get/category/specialized/stream_name_error.rb new file mode 100644 index 0000000..e8a521d --- /dev/null +++ b/test/automated/get/category/specialized/stream_name_error.rb @@ -0,0 +1,19 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Category" do + context "Specialized" do + context "Stream Name Error" do + context "Retrieving from a Stream Using Stream Name" do + stream_name = Controls::StreamName.example + + test "Is an error" do + assert_raises(Get::Category::Error) do + Get::Category.(stream_name) + end + end + end + end + end + end +end diff --git a/test/automated/get/stream/generalized/get_messages.rb b/test/automated/get/stream/generalized/get_messages.rb new file mode 100644 index 0000000..4fc5745 --- /dev/null +++ b/test/automated/get/stream/generalized/get_messages.rb @@ -0,0 +1,34 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Stream" do + context "Generalized" do + context "Get Message" do + write_message = Controls::MessageData::Write.example + + category = Controls::Category.example + + stream_name, _ = Controls::Put.(category: category) + Controls::Put.(category: category) + + message_data = Get.(stream_name) + + context "Messages Retrieved" do + test "Only messages from the specific stream" do + assert(message_data.length == 1) + end + + context "Message stream is the stream written" do + message_data.each do |md| + message_stream_name = md.stream_name + + test do + assert(message_stream_name == stream_name) + end + end + end + end + end + end + end +end diff --git a/test/automated/get/stream/generalized/specialization.rb b/test/automated/get/stream/generalized/specialization.rb new file mode 100644 index 0000000..30c6972 --- /dev/null +++ b/test/automated/get/stream/generalized/specialization.rb @@ -0,0 +1,43 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Stream" do + context "Generalized" do + context "Specialization" do + stream_name = Controls::StreamName.example + + batch_size = 1 + consumer_group_member = 0 + consumer_group_size = 1 + condition = 'global_position >= 1' + session = Session.build + + get = Get.build(stream_name, batch_size: batch_size, condition: condition, session: session) + + context "Type" do + test "Get::Stream" do + assert(get.instance_of?(Get::Stream)) + end + end + + context "Attributes" do + test "stream_name" do + assert(get.stream_name == stream_name) + end + + test "batch_size" do + assert(get.batch_size == batch_size) + end + + test "condition" do + assert(get.condition == condition) + end + + test "session" do + assert(get.session == session) + end + end + end + end + end +end diff --git a/test/automated/get/stream/get_last/get_last.rb b/test/automated/get/stream/get_last/get_last.rb new file mode 100644 index 0000000..fc7defc --- /dev/null +++ b/test/automated/get/stream/get_last/get_last.rb @@ -0,0 +1,19 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Stream" do + context "Last" do + stream_name, _ = Controls::Put.(instances: 2) + + write_message = Controls::MessageData::Write.example + + Put.(write_message, stream_name) + + last_message = Get::Stream::Last.(stream_name) + + test "Gets the last message in the stream" do + assert(last_message.data == write_message.data) + end + end + end +end diff --git a/test/automated/get/stream/get_last/no_messages.rb b/test/automated/get/stream/get_last/no_messages.rb new file mode 100644 index 0000000..998d03d --- /dev/null +++ b/test/automated/get/stream/get_last/no_messages.rb @@ -0,0 +1,18 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Stream" do + context "Last" do + context "No Messages" do + stream_name = Controls::StreamName.example + + last_message = Get::Stream::Last.(stream_name) + + test "Nil message" do + assert(last_message.nil?) + end + end + end + end +end + diff --git a/test/automated/get/stream/get_last/type.rb b/test/automated/get/stream/get_last/type.rb new file mode 100644 index 0000000..97ba970 --- /dev/null +++ b/test/automated/get/stream/get_last/type.rb @@ -0,0 +1,37 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Stream" do + context "Last" do + context "Type" do + control_type = Controls::MessageData.type + other_type = "SomeOtherType" + + stream_name, _ = Controls::Put.(instances: 2, type: control_type) + + write_message = Controls::MessageData::Write.example(type: control_type) + + Put.(write_message, stream_name) + + Controls::Put.(instances: 2, stream_name: stream_name, type: other_type) + + last_message = Get::Stream::Last.(stream_name, control_type) + + test "Gets the last message in the stream" do + assert(last_message.data == write_message.data) + end + + context "Type" do + type = last_message.type + + comment "#{type}" + detail "Control: #{control_type}" + + test do + assert(type == control_type) + end + end + end + end + end +end diff --git a/test/automated/get/stream/specialized/batch_size.rb b/test/automated/get/stream/specialized/batch_size.rb new file mode 100644 index 0000000..bbec381 --- /dev/null +++ b/test/automated/get/stream/specialized/batch_size.rb @@ -0,0 +1,19 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Stream" do + context "Specialized" do + context "Batch Size" do + stream_name, _ = Controls::Put.(instances: 3) + + messages = Get.(stream_name, batch_size: 2) + + number_of_messages = messages.length + + test "Number of messages retrieved is the specified batch size" do + assert(number_of_messages == 2) + end + end + end + end +end diff --git a/test/automated/get/stream/specialized/condition/condition.rb b/test/automated/get/stream/specialized/condition/condition.rb new file mode 100644 index 0000000..17630cb --- /dev/null +++ b/test/automated/get/stream/specialized/condition/condition.rb @@ -0,0 +1,28 @@ +require_relative '../../../../automated_init' + +context "Get" do + context "Stream" do + context "Specialized" do + context "Condition" do + stream_name, _ = Controls::Put.(instances: 3) + + condition = 'position = 0 OR position = 2' + + settings = MessageStore::Settings.build + session = Session.new + settings.set(session) + session.options = '-c message_store.sql_condition=on' + + messages = Get.(stream_name, batch_size: 3, condition: condition, session: session) + + message_positions = messages.map do |message| + message.position + end + + test "Returns messages that meet the condition" do + assert(message_positions == [0, 2]) + end + end + end + end +end diff --git a/test/automated/get/stream/specialized/condition/not_activated.rb b/test/automated/get/stream/specialized/condition/not_activated.rb new file mode 100644 index 0000000..35f876b --- /dev/null +++ b/test/automated/get/stream/specialized/condition/not_activated.rb @@ -0,0 +1,26 @@ +require_relative '../../../../automated_init' + +context "Get" do + context "Category" do + context "Specialized" do + context "Condition" do + context "Not Activated" do + stream_name = Controls::StreamName.example + + condition = 'some condition' + + settings = MessageStore::Settings.build + session = Session.new + settings.set(session) + session.options = nil + + test "Is an error" do + assert_raises(Get::Condition::Error) do + Get.(stream_name, batch_size: 3, condition: condition, session: session) + end + end + end + end + end + end +end diff --git a/test/automated/get/stream/specialized/dependency.rb b/test/automated/get/stream/specialized/dependency.rb new file mode 100644 index 0000000..4f2c297 --- /dev/null +++ b/test/automated/get/stream/specialized/dependency.rb @@ -0,0 +1,42 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Stream" do + context "Specialized" do + context "Dependency" do + write_message = Controls::MessageData::Write.example + + category = Controls::Category.example + + stream_name, _ = Controls::Put.(category: category) + Controls::Put.(category: category) + + receiver = OpenStruct.new + + Get::Stream.configure(receiver, stream_name) + + get = receiver.get + + message_data = get.() + + # message_data = Get::Stream.(stream_name) + + context "Messages Retrieved" do + test "Only messages from the specific stream" do + assert(message_data.length == 1) + end + + context "Message stream is the stream written" do + message_data.each do |md| + message_stream_name = md.stream_name + + test do + assert(message_stream_name == stream_name) + end + end + end + end + end + end + end +end diff --git a/test/automated/get/stream/specialized/get_messages.rb b/test/automated/get/stream/specialized/get_messages.rb new file mode 100644 index 0000000..dcc2c89 --- /dev/null +++ b/test/automated/get/stream/specialized/get_messages.rb @@ -0,0 +1,34 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Stream" do + context "Specialized" do + context "Get Message" do + write_message = Controls::MessageData::Write.example + + category = Controls::Category.example + + stream_name, _ = Controls::Put.(category: category) + Controls::Put.(category: category) + + message_data = Get::Stream.(stream_name) + + context "Messages Retrieved" do + test "Only messages from the specific stream" do + assert(message_data.length == 1) + end + + context "Message stream is the stream written" do + message_data.each do |md| + message_stream_name = md.stream_name + + test do + assert(message_stream_name == stream_name) + end + end + end + end + end + end + end +end diff --git a/test/automated/get/stream/specialized/no_messages.rb b/test/automated/get/stream/specialized/no_messages.rb new file mode 100644 index 0000000..e275cca --- /dev/null +++ b/test/automated/get/stream/specialized/no_messages.rb @@ -0,0 +1,17 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Stream" do + context "Specialized" do + context "No Messages" do + stream_name = Controls::StreamName.example + + batch = Get.(stream_name) + + test "Retrieves no messages" do + assert(batch == []) + end + end + end + end +end diff --git a/test/automated/get/stream/specialized/position.rb b/test/automated/get/stream/specialized/position.rb new file mode 100644 index 0000000..389490a --- /dev/null +++ b/test/automated/get/stream/specialized/position.rb @@ -0,0 +1,15 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Stream" do + context "Position" do + stream_name, _ = Controls::Put.(instances: 2) + + batch = Get.(stream_name, position: 1, batch_size: 1) + + test "Retrieves messages from the starting position" do + assert(batch.length == 1) + end + end + end +end diff --git a/test/automated/get/stream/specialized/stream_name_error.rb b/test/automated/get/stream/specialized/stream_name_error.rb new file mode 100644 index 0000000..a06ca92 --- /dev/null +++ b/test/automated/get/stream/specialized/stream_name_error.rb @@ -0,0 +1,19 @@ +require_relative '../../../automated_init' + +context "Get" do + context "Stream" do + context "Specialized" do + context "Stream Name Error" do + context "Retrieving from a Stream Using Category Name" do + category = Controls::Category.example + + test "Is an error" do + assert_raises(Get::Stream::Error) do + Get::Stream.(category) + end + end + end + end + end + end +end diff --git a/test/automated/get_last/call.rb b/test/automated/get_last/call.rb deleted file mode 100644 index 43cfcf9..0000000 --- a/test/automated/get_last/call.rb +++ /dev/null @@ -1,38 +0,0 @@ -require_relative '../automated_init' - -context "Get Last" do - context "Call" do - stream_name = Controls::StreamName.example - type = Controls::MessageData.type - - context "Not Specialized" do - cls = Class.new do - include MessageStore::Get::Stream::Last - end - - test "Raises virtual method error" do - assert_raises(Virtual::PureMethod::Error) do - cls.(stream_name) - end - end - end - - context "Specialized" do - specialized_method_executed = false - - cls = Class.new do - include MessageStore::Get::Stream::Last - - define_method(:call) do |_stream_name, type=nil| - specialized_method_executed = true if _stream_name == stream_name - end - end - - cls.(stream_name, type) - - test "Executes specialized method" do - assert(specialized_method_executed) - end - end - end -end diff --git a/test/automated/get_last/configure.rb b/test/automated/get_last/configure.rb index b943da5..fd0966c 100644 --- a/test/automated/get_last/configure.rb +++ b/test/automated/get_last/configure.rb @@ -7,7 +7,7 @@ receiver = OpenStruct.new - Controls::GetLast::Example.configure(receiver, session: session) + Get::Stream::Last.configure(receiver, session: session) get_last = receiver.get_last @@ -19,19 +19,19 @@ context "Session Not Given" do receiver = OpenStruct.new - Controls::GetLast::Example.configure(receiver) + Get::Stream::Last.configure(receiver) get_last = receiver.get_last test "Default session is used" do - assert(get_last.session == Controls::GetLast.default_session) + refute(get_last.session.nil?) end end context "Attribute Name Specified" do receiver = OpenStruct.new - Controls::GetLast::Example.configure(receiver, attr_name: :some_attr) + Get::Stream::Last.configure(receiver, attr_name: :some_attr) get_last = receiver.some_attr diff --git a/test/automated/get_last/substitute.rb b/test/automated/get_last/substitute.rb index 508a5f7..1eb598f 100644 --- a/test/automated/get_last/substitute.rb +++ b/test/automated/get_last/substitute.rb @@ -7,7 +7,7 @@ control_message_data = Controls::MessageData::Read.example context "Message Is Set" do - substitute = SubstAttr::Substitute.build(Controls::GetLast::Example) + substitute = SubstAttr::Substitute.build(Get::Stream::Last) substitute.set(stream_name, control_message_data) @@ -19,7 +19,7 @@ end context "Message Not Set" do - substitute = SubstAttr::Substitute.build(Controls::GetLast::Example) + substitute = SubstAttr::Substitute.build(Get::Stream::Last) message_data = substitute.(stream_name) diff --git a/test/automated/iterator/category/next.rb b/test/automated/iterator/category/next.rb new file mode 100644 index 0000000..ef470e1 --- /dev/null +++ b/test/automated/iterator/category/next.rb @@ -0,0 +1,27 @@ +require_relative '../../automated_init' + +context "Iterator" do + context "Category" do + context "Next" do + stream_name, _ = Controls::Put.() + + category = StreamName.get_category(stream_name) + + Controls::Put.(category: category) + + iterator = Read::Iterator.build + Get.configure(iterator, category, batch_size: 1) + + batch = [] + + 2.times do + message_data = iterator.next + batch << message_data unless message_data.nil? + end + + test "Gets each message" do + assert(batch.length == 2) + end + end + end +end diff --git a/test/automated/iterator/category/no_further_message_data.rb b/test/automated/iterator/category/no_further_message_data.rb new file mode 100644 index 0000000..1ae7b32 --- /dev/null +++ b/test/automated/iterator/category/no_further_message_data.rb @@ -0,0 +1,24 @@ +require_relative '../../automated_init' + +context "Iterator" do + context "Category" do + context "No Further Message Data" do + stream_name, _ = Controls::Put.() + + category = StreamName.get_category(stream_name) + + Controls::Put.(category: category) + + iterator = Read::Iterator.build + Get.configure(iterator, category, batch_size: 1) + + 2.times { iterator.next } + + last = iterator.next + + test "Results in nil" do + assert(last.nil?) + end + end + end +end diff --git a/test/automated/iterator/stream/next.rb b/test/automated/iterator/stream/next.rb new file mode 100644 index 0000000..74c44b5 --- /dev/null +++ b/test/automated/iterator/stream/next.rb @@ -0,0 +1,23 @@ +require_relative '../../automated_init' + +context "Iterator" do + context "Stream" do + context "Next" do + stream_name, _ = Controls::Put.(instances: 2) + + iterator = Read::Iterator.build + Get.configure(iterator, stream_name, batch_size: 1) + + batch = [] + + 2.times do + message_data = iterator.next + batch << message_data unless message_data.nil? + end + + test "Gets each message" do + assert(batch.length == 2) + end + end + end +end diff --git a/test/automated/iterator/stream/no_further_message_data.rb b/test/automated/iterator/stream/no_further_message_data.rb new file mode 100644 index 0000000..99ffa10 --- /dev/null +++ b/test/automated/iterator/stream/no_further_message_data.rb @@ -0,0 +1,20 @@ +require_relative '../../automated_init' + +context "Iterator" do + context "Stream" do + context "No Further Message Data" do + stream_name, _ = Controls::Put.(instances: 2) + + iterator = Read::Iterator.build + Get.configure(iterator, stream_name, batch_size: 1) + + 2.times { iterator.next } + + last = iterator.next + + test "Results in nil" do + assert(last.nil?) + end + end + end +end diff --git a/test/automated/iterator_tests.rb b/test/automated/iterator_tests.rb deleted file mode 100644 index af5211b..0000000 --- a/test/automated/iterator_tests.rb +++ /dev/null @@ -1,6 +0,0 @@ -require_relative './automated_init' - -TestBench::CLI::Run.( - 'test/automated/iterator', - exclude_file_pattern: %r{/_|sketch|(_init\.rb|_tests\.rb)\z} -) diff --git a/test/automated/put/category_as_stream_name.rb b/test/automated/put/category_as_stream_name.rb new file mode 100644 index 0000000..8144dba --- /dev/null +++ b/test/automated/put/category_as_stream_name.rb @@ -0,0 +1,16 @@ +require_relative '../automated_init' + +context "Put" do + context "Category as Stream Name" do + category = Controls::Category.example + write_message = Controls::MessageData::Write.example + + Put.(write_message, category) + + read_message = Get.(category).first + + test "Writes the category name as the stream name" do + assert(read_message.stream_name == category) + end + end +end diff --git a/test/automated/put/data/empty.rb b/test/automated/put/data/empty.rb new file mode 100644 index 0000000..717c4e5 --- /dev/null +++ b/test/automated/put/data/empty.rb @@ -0,0 +1,21 @@ +require_relative '../../automated_init' + +context "Put" do + context "Metadata" do + context "Nil" do + stream_name = Controls::StreamName.example + + write_message = Controls::MessageData::Write.example(data: {}) + + position = Put.(write_message, stream_name) + + read_message = Get.(stream_name, position: position).first + + context "Read metadata" do + test "Is nil" do + assert(read_message.data.nil?) + end + end + end + end +end diff --git a/test/automated/put/data/nil.rb b/test/automated/put/data/nil.rb new file mode 100644 index 0000000..b9c89e6 --- /dev/null +++ b/test/automated/put/data/nil.rb @@ -0,0 +1,20 @@ +require_relative '../../automated_init' + +context "Put" do + context "Data" do + context "Nil" do + stream_name = Controls::StreamName.example + write_message = Controls::MessageData::Write.example(data: :none) + + position = Put.(write_message, stream_name) + + read_message = Get.(stream_name, position: position).first + + context "Read metadata" do + test "Is nil" do + assert(read_message.data.nil?) + end + end + end + end +end diff --git a/test/automated/put/expected_version/error.rb b/test/automated/put/expected_version/error.rb new file mode 100644 index 0000000..2a257d0 --- /dev/null +++ b/test/automated/put/expected_version/error.rb @@ -0,0 +1,28 @@ +require_relative '../../automated_init' + +context "Put" do + context "Expected Version" do + context "Does not match the stream version" do + stream_name = Controls::StreamName.example + + write_message = Controls::MessageData::Write.example + position = Put.(write_message, stream_name) + + incorrect_stream_version = position + 1 + + test "Is an error" do + assert_raises(ExpectedVersion::Error) do + Put.(write_message, stream_name, expected_version: incorrect_stream_version ) + end + end + + context "Message" do + read_message = Get.(stream_name, position: incorrect_stream_version, batch_size: 1).first + + test "Is not written" do + assert(read_message.nil?) + end + end + end + end +end diff --git a/test/automated/put/expected_version/expected_version.rb b/test/automated/put/expected_version/expected_version.rb new file mode 100644 index 0000000..9d96c6d --- /dev/null +++ b/test/automated/put/expected_version/expected_version.rb @@ -0,0 +1,23 @@ +require_relative '../../automated_init' + +context "Put" do + context "Message" do + context "Expected Version" do + stream_name = Controls::StreamName.example + + write_message_1 = Controls::MessageData::Write.example + + position = Put.(write_message_1, stream_name) + + write_message_2 = Controls::MessageData::Write.example + + Put.(write_message_2, stream_name, expected_version: position) + + read_message = Get.(stream_name, position: position + 1).first + + test "Got the message that was written" do + assert(read_message.data == write_message_2.data) + end + end + end +end diff --git a/test/automated/put/metadata/empty.rb b/test/automated/put/metadata/empty.rb new file mode 100644 index 0000000..d83e903 --- /dev/null +++ b/test/automated/put/metadata/empty.rb @@ -0,0 +1,23 @@ +require_relative '../../automated_init' + +context "Put" do + context "Metadata" do + context "Nil" do + stream_name = Controls::StreamName.example + + write_message = Controls::MessageData::Write.example(metadata: {}) + + # write_message.metadata = {} + + position = Put.(write_message, stream_name) + + read_message = Get.(stream_name, position: position).first + + context "Read metadata" do + test "Is nil" do + assert(read_message.metadata.nil?) + end + end + end + end +end diff --git a/test/automated/put/metadata/nil.rb b/test/automated/put/metadata/nil.rb new file mode 100644 index 0000000..9069e1d --- /dev/null +++ b/test/automated/put/metadata/nil.rb @@ -0,0 +1,21 @@ +require_relative '../../automated_init' + +context "Put" do + context "Metadata" do + context "Nil" do + stream_name = Controls::StreamName.example + + write_message = Controls::MessageData::Write.example(metadata: :none) + + position = Put.(write_message, stream_name) + + read_message = Get.(stream_name, position: position).first + + context "Read metadata" do + test "Is nil" do + assert(read_message.metadata.nil?) + end + end + end + end +end diff --git a/test/automated/put/missing_id.rb b/test/automated/put/missing_id.rb new file mode 100644 index 0000000..d99e41c --- /dev/null +++ b/test/automated/put/missing_id.rb @@ -0,0 +1,22 @@ +require_relative '../automated_init' + +context "Put" do + context "Missing ID" do + stream_name = Controls::StreamName.example + write_message = Controls::MessageData::Write.example(id: :none) + + position = Put.(write_message, stream_name) + + read_message = Get.(stream_name, position: position).first + + context "An ID is assigned to the message" do + test "Write message" do + refute(write_message.id.nil?) + end + + test "Read message" do + refute(read_message.id.nil?) + end + end + end +end diff --git a/test/automated/put/no_stream/existing_stream.rb b/test/automated/put/no_stream/existing_stream.rb new file mode 100644 index 0000000..a2989a2 --- /dev/null +++ b/test/automated/put/no_stream/existing_stream.rb @@ -0,0 +1,20 @@ +require_relative '../../automated_init' + +context "Put" do + context "No Stream" do + context "Existing Stream" do + stream_name = Controls::StreamName.example + + write_message_1 = Controls::MessageData::Write.example + write_message_2 = Controls::MessageData::Write.example + + Put.(write_message_1, stream_name) + + test "Is an error" do + assert_raises(ExpectedVersion::Error) do + Put.(write_message_2, stream_name, expected_version: NoStream.name) + end + end + end + end +end diff --git a/test/automated/put/no_stream/no_stream.rb b/test/automated/put/no_stream/no_stream.rb new file mode 100644 index 0000000..6b47a07 --- /dev/null +++ b/test/automated/put/no_stream/no_stream.rb @@ -0,0 +1,16 @@ +require_relative '../../automated_init' + +context "Put" do + context "No Stream" do + context "For a stream that doesn't exist" do + stream_name = Controls::StreamName.example + write_message = Controls::MessageData::Write.example + + position = Put.(write_message, stream_name) + + test "Ensures that the message written is the first message in the stream" do + assert(position == 0) + end + end + end +end diff --git a/test/automated/put/put.rb b/test/automated/put/put.rb new file mode 100644 index 0000000..4bdcbbf --- /dev/null +++ b/test/automated/put/put.rb @@ -0,0 +1,44 @@ +require_relative '../automated_init' + +context "Put and Get" do + stream_name = Controls::StreamName.example + write_message = Controls::MessageData::Write.example + + position = Put.(write_message, stream_name) + + read_message = Get.(stream_name, position: position).first + + context "Got the message that was written" do + test "ID" do + assert(read_message.id == write_message.id) + end + + test "Type" do + assert(read_message.type == write_message.type) + end + + test "Data" do + assert(read_message.data == write_message.data) + end + + test "Metadata" do + assert(read_message.metadata == write_message.metadata) + end + + test "Stream Name" do + assert(read_message.stream_name == stream_name) + end + + test "Position" do + assert(read_message.position == position) + end + + test "Global Position" do + assert(read_message.global_position.is_a? Numeric) + end + + test "Recorded Time" do + assert(read_message.time.is_a? Time) + end + end +end diff --git a/test/automated/put/returns_stream_position.rb b/test/automated/put/returns_stream_position.rb new file mode 100644 index 0000000..1cff268 --- /dev/null +++ b/test/automated/put/returns_stream_position.rb @@ -0,0 +1,15 @@ +require_relative '../automated_init' + +context "Put" do + context "Returns Stream Position" do + stream_name = Controls::StreamName.example + + write_message = Controls::MessageData::Write.example + + position = Put.(write_message, stream_name) + + test "Result is stream position" do + refute(position.nil?) + end + end +end diff --git a/test/automated/put/stream_position_increases.rb b/test/automated/put/stream_position_increases.rb new file mode 100644 index 0000000..0c45bcf --- /dev/null +++ b/test/automated/put/stream_position_increases.rb @@ -0,0 +1,17 @@ +require_relative '../automated_init' + +context "Put" do + context "Stream Position Increases with Subsequent Writes" do + stream_name = Controls::StreamName.example + + write_message_1 = Controls::MessageData::Write.example + write_message_2 = Controls::MessageData::Write.example + + position_1 = Put.(write_message_1, stream_name) + position_2 = Put.(write_message_2, stream_name) + + test "First version is one less than the second version" do + assert(position_1 + 1 == position_2) + end + end +end diff --git a/test/automated/put_and_get.rb b/test/automated/put_and_get.rb new file mode 100644 index 0000000..9f3fdc0 --- /dev/null +++ b/test/automated/put_and_get.rb @@ -0,0 +1,44 @@ +require_relative 'automated_init' + +context "Put and Get" do + stream_name = Controls::StreamName.example + write_message = Controls::MessageData::Write.example + + position = Put.(write_message, stream_name) + + read_message = Get.(stream_name, position: position).first + + context "Got the message that was written" do + test "ID" do + assert(read_message.id == write_message.id) + end + + test "Type" do + assert(read_message.type == write_message.type) + end + + test "Data" do + assert(read_message.data == write_message.data) + end + + test "Metadata" do + assert(read_message.metadata == write_message.metadata) + end + + test "Stream Name" do + assert(read_message.stream_name == stream_name) + end + + test "Position" do + assert(read_message.position == position) + end + + test "Global Position" do + assert(read_message.global_position.is_a? Numeric) + end + + test "Recorded Time" do + assert(read_message.time.is_a? Time) + end + end +end diff --git a/test/automated/read/condition.rb b/test/automated/read/condition.rb new file mode 100644 index 0000000..a3ecf75 --- /dev/null +++ b/test/automated/read/condition.rb @@ -0,0 +1,24 @@ +require_relative '../automated_init' + +context "Read" do + context "Condition" do + stream_name, _ = Controls::Put.(instances: 3) + + condition = 'position = 0' + + message_count = 0 + + settings = MessageStore::Settings.build + session = Session.new + settings.set(session) + session.options = '-c message_store.sql_condition=on' + + Read.(stream_name, condition: condition, session: session) do + message_count += 1 + end + + test "Reads messages that meet condition" do + assert(message_count == 1) + end + end +end diff --git a/test/automated/read/default_batch_size.rb b/test/automated/read/default_batch_size.rb new file mode 100644 index 0000000..507372a --- /dev/null +++ b/test/automated/read/default_batch_size.rb @@ -0,0 +1,11 @@ +require_relative '../automated_init' + +context "Read" do + context "Default batch Size" do + default_batch_size = MessageStore::Read::Defaults.batch_size + + test "Is the Get implementation's default batch size" do + assert(default_batch_size == MessageStore::Get::Defaults.batch_size) + end + end +end diff --git a/test/automated/read/position.rb b/test/automated/read/position.rb new file mode 100644 index 0000000..c27ad82 --- /dev/null +++ b/test/automated/read/position.rb @@ -0,0 +1,17 @@ +require_relative '../automated_init' + +context "Read" do + context "Position" do + stream_name, _ = Controls::Put.(instances: 2) + + batch = [] + + Read.(stream_name, position: 1, batch_size: 1) do |message_data| + batch << message_data + end + + test "Reads from the starting position" do + assert(batch.length == 1) + end + end +end diff --git a/test/automated/read/read.rb b/test/automated/read/read.rb new file mode 100644 index 0000000..6bf03c9 --- /dev/null +++ b/test/automated/read/read.rb @@ -0,0 +1,15 @@ +require_relative '../automated_init' + +context "Read" do + stream_name, _ = Controls::Put.(instances: 2) + + batch = [] + + Read.(stream_name, batch_size: 1) do |message_data| + batch << message_data + end + + test "Reads batches of messages" do + assert(batch.length == 2) + end +end diff --git a/test/automated/session/build.rb b/test/automated/session/build.rb new file mode 100644 index 0000000..6f3e3b8 --- /dev/null +++ b/test/automated/session/build.rb @@ -0,0 +1,25 @@ +require_relative '../automated_init' + +context "Session" do + context "Build" do + context "Settings is specified" do + settings = MessageStore::Settings.build + + session = Session.build(settings: settings) + + test "Specified settings is used" do + assert(session.user == settings.get(:user)) + end + end + + context "Settings is not specified" do + settings = MessageStore::Settings.build + + session = Session.build + + test "Settings is built" do + assert(session.user == settings.get(:user)) + end + end + end +end diff --git a/test/automated/session/close.rb b/test/automated/session/close.rb new file mode 100644 index 0000000..83d3ae2 --- /dev/null +++ b/test/automated/session/close.rb @@ -0,0 +1,46 @@ +require_relative "../automated_init" + +context "Session" do + context "Close" do + context "Session is Open" do + session = Session.build + session.open + + assert(session.open?) + + session.close + + test "No longer connected" do + refute(session.connected?) + end + + context "Connection Attribute" do + connection = session.connection + + test "Not set" do + assert(connection.nil?) + end + end + end + + context "Session is Closed" do + session = Session.build + + refute(session.open?) + + session.close + + test "Not connected" do + refute(session.connected?) + end + + context "Connection Attribute" do + connection = session.connection + + test "Not set" do + assert(connection.nil?) + end + end + end + end +end diff --git a/test/automated/session/configure.rb b/test/automated/session/configure.rb new file mode 100644 index 0000000..12927b2 --- /dev/null +++ b/test/automated/session/configure.rb @@ -0,0 +1,47 @@ +require_relative '../automated_init' + +context "Session" do + context "Configure" do + context "Session is specified" do + receiver = OpenStruct.new + settings = Object.new + session = Object.new + + Session.configure(receiver, session: session) + + test "Specified session is used" do + assert(receiver.session == session) + end + end + + context "Settings is specified" do + receiver = OpenStruct.new + + user = SecureRandom.hex + + settings_data = { + user: user + } + + settings = MessageStore::Settings.build(settings_data) + + Session.configure(receiver, settings: settings) + + test "Session is built from settings" do + assert(receiver.session.user == settings.get('user')) + end + end + + context "Specifying both the session and settings" do + receiver = OpenStruct.new + settings = Object.new + session = Object.new + + test "Is an error" do + assert_raises(Session::Error) do + Session.configure(receiver, settings: settings, session: session) + end + end + end + end +end diff --git a/test/automated/session/connect_on_first_use.rb b/test/automated/session/connect_on_first_use.rb new file mode 100644 index 0000000..4e6db9b --- /dev/null +++ b/test/automated/session/connect_on_first_use.rb @@ -0,0 +1,15 @@ +require_relative '../automated_init' + +context "Session" do + test "On First Use" do + session = Session.build + + refute(session.connected?) + + test "Connects" do + refute_raises do + session.execute(';') + end + end + end +end diff --git a/test/automated/session/connection_executed_time.rb b/test/automated/session/connection_executed_time.rb new file mode 100644 index 0000000..ec0e5f9 --- /dev/null +++ b/test/automated/session/connection_executed_time.rb @@ -0,0 +1,27 @@ +require_relative '../automated_init' + +context "Session" do + context "Connection Executed Time" do + context "Before First Execution" do + session = Session.build + + test "Executed time isn't set" do + assert(session.executed_time.nil?) + end + end + + context "After Execution" do + session = Session.build + Dependency::Substitute.(:clock, session) + + time = Controls::Time::Raw.example + session.clock.now = time + + session.execute(';') + + test "Executed time is set to the clock time" do + assert(session.executed_time == time) + end + end + end +end diff --git a/test/automated/session/connection_executed_time_elapsed_milliseconds.rb b/test/automated/session/connection_executed_time_elapsed_milliseconds.rb new file mode 100644 index 0000000..e996241 --- /dev/null +++ b/test/automated/session/connection_executed_time_elapsed_milliseconds.rb @@ -0,0 +1,29 @@ +require_relative '../automated_init' + +context "Session" do + context "Connection Executed Time Elapsed Milliseconds" do + context "Before First Execution" do + session = Session.new + + test "Is nil" do + assert(session.executed_time_elapsed_milliseconds.nil?) + end + end + + context "After Execution" do + session = Session.new + + start_time = Controls::Time::Raw.example + end_time = start_time + 1 + + session.executed_time = start_time + session.clock.now = end_time + + elapsed_milliseconds = (end_time - start_time) * 1000 + + test "Is difference between current time and executed time" do + assert(session.executed_time_elapsed_milliseconds == elapsed_milliseconds) + end + end + end +end diff --git a/test/automated/session/escape.rb b/test/automated/session/escape.rb new file mode 100644 index 0000000..d632637 --- /dev/null +++ b/test/automated/session/escape.rb @@ -0,0 +1,22 @@ +require_relative '../automated_init' + +context "Session" do + context "Escape" do + session = Session.build + + unescaped_data = "'" + control_data = "''" + + escaped_data = session.escape(unescaped_data) + + context "Escaped Data" do + comment escaped_data.inspect + detail "Control Data: #{control_data.inspect}" + detail "Unescaped Data: #{unescaped_data.inspect}" + + test "escaped_Data is escaped" do + assert(escaped_data == control_data) + end + end + end +end diff --git a/test/automated/session/initial_state_is_not_connected.rb b/test/automated/session/initial_state_is_not_connected.rb new file mode 100644 index 0000000..01cd50d --- /dev/null +++ b/test/automated/session/initial_state_is_not_connected.rb @@ -0,0 +1,12 @@ +require_relative '../automated_init' + +context "Session" do + test "Initial State" do + session = Session.build + connected = session.connected? + + test "Not connected" do + refute(connected) + end + end +end diff --git a/test/automated/session/settings.rb b/test/automated/session/settings.rb new file mode 100644 index 0000000..39eba74 --- /dev/null +++ b/test/automated/session/settings.rb @@ -0,0 +1,21 @@ +require_relative '../automated_init' + +context "Session" do + context "Settings" do + session = Session.build + + settings = MessageStore::Settings.build + settings_hash = settings.get.to_h + + names = MessageStore::Settings.names + + names.each do |name| + test "#{name}" do + session_val = session.public_send(name) + settings_val = settings_hash[name.to_s] + + assert(session_val == settings_val) + end + end + end +end diff --git a/test/automated/settings.rb b/test/automated/settings.rb new file mode 100644 index 0000000..b7824df --- /dev/null +++ b/test/automated/settings.rb @@ -0,0 +1,17 @@ +require_relative 'automated_init' + +context "Settings" do + settings = MessageStore::Settings.build + + context "Names" do + settings_hash = settings.get.to_h + + names = MessageStore::Settings.names + + names.each do |name| + test "#{name}" do + assert(settings_hash.has_key? name.to_s) + end + end + end +end diff --git a/test/automated/settings/default_path.rb b/test/automated/settings/default_path.rb new file mode 100644 index 0000000..9d282b1 --- /dev/null +++ b/test/automated/settings/default_path.rb @@ -0,0 +1,18 @@ +require_relative '../automated_init' + +context "Settings" do + context "Default Path" do + prior_default_settings_path = ENV['MESSAGE_STORE_SETTINGS_PATH'] + + overridden_path = 'some_path' + ENV['MESSAGE_STORE_SETTINGS_PATH'] = overridden_path + + settings_path = MessageStore::Settings.data_source + + test "Overridden by MESSAGE_STORE_SETTINGS_PATH environment variable" do + assert(settings_path == overridden_path) + end + + ENV['MESSAGE_STORE_SETTINGS_PATH'] = prior_default_settings_path + end +end diff --git a/test/automated/write/batch/expected_version/error.rb b/test/automated/write/batch/expected_version/error.rb new file mode 100644 index 0000000..adca25a --- /dev/null +++ b/test/automated/write/batch/expected_version/error.rb @@ -0,0 +1,37 @@ +require_relative '../../../automated_init' + +context "Write" do + context "Batch" do + context "Expected Version" do + context "Does not match the stream version" do + stream_name = Controls::StreamName.example + + write_message = Controls::MessageData::Write.example + position = Write.(write_message, stream_name) + + incorrect_stream_version = position + 1 + + write_message_1 = Controls::MessageData::Write.example + write_message_2 = Controls::MessageData::Write.example + + batch = [write_message_1, write_message_2] + + test "Is an error" do + assert_raises(ExpectedVersion::Error) do + Write.(batch, stream_name, expected_version: incorrect_stream_version) + end + end + + context "Messages" do + 2.times do |i| + read_message = Get.(stream_name, position: i + 1, batch_size: 1).first + + test "Message #{i + 1} not written" do + assert(read_message.nil?) + end + end + end + end + end + end +end diff --git a/test/automated/write/batch/expected_version/expected_version.rb b/test/automated/write/batch/expected_version/expected_version.rb new file mode 100644 index 0000000..de922b2 --- /dev/null +++ b/test/automated/write/batch/expected_version/expected_version.rb @@ -0,0 +1,30 @@ +require_relative '../../../automated_init' + +context "Write" do + context "Batch" do + context "Expected Version" do + stream_name = Controls::StreamName.example + + write_message = Controls::MessageData::Write.example + position = Write.(write_message, stream_name) + + write_message_1 = Controls::MessageData::Write.example + write_message_2 = Controls::MessageData::Write.example + + batch = [write_message_1, write_message_2] + + Write.(batch, stream_name, expected_version: position) + + context "Individual Messages are Written" do + 2.times do |i| + read_message = Get.(stream_name, position: i + 1, batch_size: 1).first + write_message = batch[i] + + test "Message #{i + 1}" do + assert(read_message.data == write_message.data) + end + end + end + end + end +end diff --git a/test/automated/write/batch/write.rb b/test/automated/write/batch/write.rb new file mode 100644 index 0000000..d053b5f --- /dev/null +++ b/test/automated/write/batch/write.rb @@ -0,0 +1,29 @@ +require_relative '../../automated_init' + +context "Write" do + context "Batch" do + stream_name = Controls::StreamName.example + + write_message_1 = Controls::MessageData::Write.example + write_message_2 = Controls::MessageData::Write.example + + batch = [write_message_1, write_message_2] + + last_written_position = Write.(batch, stream_name) + + test "Last written position" do + assert(last_written_position == 1) + end + + context "Individual Messages are Written" do + 2.times do |i| + read_message = Get.(stream_name, position: i, batch_size: 1).first + write_message = batch[i] + + test "Message #{i + 1}" do + assert(read_message.data == write_message.data) + end + end + end + end +end diff --git a/test/automated/write/ids_are_assigned.rb b/test/automated/write/ids_are_assigned.rb index d75a3d2..f3893bb 100644 --- a/test/automated/write/ids_are_assigned.rb +++ b/test/automated/write/ids_are_assigned.rb @@ -11,7 +11,10 @@ batch = [write_message_1, write_message_2, write_message_3] - Controls::Write::Example.(batch, stream_name) + ## Review - Aaron, Sat Jan 21 2023 + write = Write.new + write.put = Put.new + write.(batch, stream_name) context "Missing IDs Are Assigned" do test "Message 1" do diff --git a/test/automated/write/message/expected_version/error.rb b/test/automated/write/message/expected_version/error.rb new file mode 100644 index 0000000..54603d6 --- /dev/null +++ b/test/automated/write/message/expected_version/error.rb @@ -0,0 +1,30 @@ +require_relative '../../../automated_init' + +context "Write" do + context "Message" do + context "Expected Version" do + context "Does not match the stream version" do + stream_name = Controls::StreamName.example + + write_message = Controls::MessageData::Write.example + position = Write.(write_message, stream_name) + + incorrect_stream_version = position + 1 + + test "Is an error" do + assert_raises(ExpectedVersion::Error) do + Write.(write_message, stream_name, expected_version: incorrect_stream_version ) + end + end + + context "Message" do + read_message = Get.(stream_name, position: incorrect_stream_version, batch_size: 1).first + + test "Is not written" do + assert(read_message.nil?) + end + end + end + end + end +end diff --git a/test/automated/write/message/expected_version/expected_version.rb b/test/automated/write/message/expected_version/expected_version.rb new file mode 100644 index 0000000..a353879 --- /dev/null +++ b/test/automated/write/message/expected_version/expected_version.rb @@ -0,0 +1,23 @@ +require_relative '../../../automated_init' + +context "Write" do + context "Message" do + context "Expected Version" do + stream_name = Controls::StreamName.example + + write_message_1 = Controls::MessageData::Write.example + + position = Write.(write_message_1, stream_name) + + write_message_2 = Controls::MessageData::Write.example + + Write.(write_message_2, stream_name, expected_version: position) + + read_message = Get.(stream_name, position: position + 1).first + + test "Got the message that was written" do + assert(read_message.data == write_message_2.data) + end + end + end +end diff --git a/test/automated/write/message/write.rb b/test/automated/write/message/write.rb new file mode 100644 index 0000000..ea43ff3 --- /dev/null +++ b/test/automated/write/message/write.rb @@ -0,0 +1,17 @@ +require_relative '../../automated_init' + +context "Write" do + context "Message" do + stream_name = Controls::StreamName.example + + write_message = Controls::MessageData::Write.example + + position = Write.(write_message, stream_name) + + read_message = Get.(stream_name, position: position).first + + test "Got the message that was written" do + assert(read_message.data == write_message.data) + end + end +end diff --git a/test/benchmark/benchmark_init.rb b/test/benchmark/benchmark_init.rb new file mode 100644 index 0000000..27195c8 --- /dev/null +++ b/test/benchmark/benchmark_init.rb @@ -0,0 +1,11 @@ +require_relative '../test_init' + +Dependency.activate +Initializer.activate + +require 'diagnostics/sample' +require 'fileutils' +require 'pathname' + +require_relative 'defaults' +require_relative 'record_result' diff --git a/test/benchmark/defaults.rb b/test/benchmark/defaults.rb new file mode 100644 index 0000000..cc0e0ad --- /dev/null +++ b/test/benchmark/defaults.rb @@ -0,0 +1,45 @@ +module Test + module Benchmark + class Defaults + initializer :cycles, :warmup_cycles, :gc, :stream_name, :verbose + + def total_cycles + cycles + warmup_cycles + end + + def to_s + <<~TEXT + Cycles: #{cycles} + Warmup Cycles: #{warmup_cycles} + GC: #{gc} + Stream Name: #{stream_name.inspect} + TEXT + end + + def self.build + new(cycles, warmup_cycles, gc, stream_name, verbose) + end + + def self.cycles + Integer(ENV['CYCLES'] || 100_000) + end + + def self.warmup_cycles + Integer(ENV['WARMUP_CYCLES'] || 10) + end + + def self.gc + ['on', 'true'].include?(ENV['GC']) ? true : false + end + + def self.verbose + ['on', 'true'].include?(ENV['VERBOSE']) ? true : false + end + + def self.stream_name +## ENV['STREAM_NAME'] || Controls::StreamName.example + ENV['STREAM_NAME'] + end + end + end +end diff --git a/test/benchmark/get_category.rb b/test/benchmark/get_category.rb new file mode 100644 index 0000000..d137f6b --- /dev/null +++ b/test/benchmark/get_category.rb @@ -0,0 +1,47 @@ +require_relative 'benchmark_init' + +defaults = Test::Benchmark::Defaults.build + +puts +puts "Get Category Benchmark (#{__FILE__})" +puts + +puts "» defaults" +puts defaults.to_s +puts + +total_cycles = defaults.total_cycles + +puts "» constructing #{total_cycles} entries" +list = Controls::MessageData::Write::List.get(instances: total_cycles, category: defaults.stream_name) + +put = Put.build + +puts "» writing entries" +list.each do |entry| + if defaults.verbose + puts "Stream: #{entry.stream_name}, Category: #{entry.category}" + end + + put.(entry.message_data, entry.stream_name) +end + +puts "» constructing Get::Category" +get = Get::Category.build('', batch_size: 1) + +puts "» executing and sampling #{total_cycles} cycles" +result = Diagnostics::Sample.(defaults.cycles, warmup_cycles: defaults.warmup_cycles, gc: defaults.gc) do |i| + entry = list[i] + + if defaults.verbose + puts "Getting Category: #{entry.category}" + end + + get.(0, stream_name: entry.category) +end + +puts +filename = Benchmark::RecordResult.('Get Category Benchmark', result) +puts +puts filename +puts diff --git a/test/benchmark/get_stream.rb b/test/benchmark/get_stream.rb new file mode 100644 index 0000000..486332d --- /dev/null +++ b/test/benchmark/get_stream.rb @@ -0,0 +1,47 @@ +require_relative 'benchmark_init' + +defaults = Test::Benchmark::Defaults.build + +puts +puts "Get Stream Benchmark (#{__FILE__})" +puts + +puts "» defaults" +puts defaults.to_s +puts + +total_cycles = defaults.total_cycles + +puts "» constructing #{total_cycles} entries" +list = Controls::MessageData::Write::List.get(instances: total_cycles, stream_name: defaults.stream_name) + +put = Put.build + +puts "» writing entries" +list.each do |entry| + if defaults.verbose + puts "Stream: #{entry.stream_name}, Category: #{entry.category}" + end + + put.(entry.message_data, entry.stream_name) +end + +puts "» constructing Get::Stream" +get = Get::Stream.build('-', batch_size: 1) + +puts "» executing and sampling #{total_cycles} cycles" +result = Diagnostics::Sample.(defaults.cycles, warmup_cycles: defaults.warmup_cycles, gc: defaults.gc) do |i| + entry = list[i] + + if defaults.verbose + puts "Getting Stream: #{entry.stream_name}" + end + + get.(0, stream_name: entry.stream_name) +end + +puts +filename = Benchmark::RecordResult.('Get Stream Benchmark', result) +puts +puts filename +puts diff --git a/test/benchmark/put.rb b/test/benchmark/put.rb new file mode 100644 index 0000000..d42b806 --- /dev/null +++ b/test/benchmark/put.rb @@ -0,0 +1,36 @@ +require_relative 'benchmark_init' + +defaults = Test::Benchmark::Defaults.build + +puts +puts "Put Benchmark (#{__FILE__})" +puts + +puts "» defaults" +puts defaults.to_s +puts + +total_cycles = defaults.total_cycles + +puts "» constructing #{total_cycles} entries" +list = Controls::MessageData::Write::List.get(instances: total_cycles, stream_name: defaults.stream_name) + +puts "» constructing Put" +put = Put.build + +puts "» executing and sampling #{total_cycles} cycles" +result = Diagnostics::Sample.(defaults.cycles, warmup_cycles: defaults.warmup_cycles, gc: defaults.gc) do |i| + entry = list[i] + + if defaults.verbose + puts "Putting Stream: #{entry.stream_name}" + end + + put.(entry.message_data, entry.stream_name) +end + +puts +filename = Benchmark::RecordResult.('Put Benchmark', result) +puts +puts filename +puts diff --git a/test/benchmark/record_result.rb b/test/benchmark/record_result.rb new file mode 100644 index 0000000..ca0335c --- /dev/null +++ b/test/benchmark/record_result.rb @@ -0,0 +1,59 @@ +module Benchmark + class RecordResult + dependency :clock + + initializer :name, :content + + def self.build(name, content) + instance = new(name, content) + instance.configure + instance + end + + def self.call(name, content) + instance = build(name, content) + instance.() + end + + def call + digest, filename = write_result_file + + puts digest + + filename.to_s + end + + def write_result_file + fn = filename + + FileUtils.mkdir_p(fn.dirname) + + d = digest + fn.write(d) + + [d, fn] + end + + def filename + time = clock.iso8601.gsub(':','-') + directory = Pathname.new('test/benchmark/tmp') + filename = directory.join("#{name} - #{time} - #{RUBY_DESCRIPTION}.txt") + + filename + end + + def digest + <<~TEXT + #{name} + - - - + #{content} + #{RUBY_DESCRIPTION} + TEXT + .chomp + end + + def configure + Clock::UTC.configure(self) + end + end +end diff --git a/test/benchmark/results/Get Benchmark - 2018-06-05T02-03-42.080Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt b/test/benchmark/results/Get Benchmark - 2018-06-05T02-03-42.080Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt new file mode 100644 index 0000000..bf7866e --- /dev/null +++ b/test/benchmark/results/Get Benchmark - 2018-06-05T02-03-42.080Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt @@ -0,0 +1,23 @@ +Get Benchmark +- - - +Cycles: 100000 +Time: 77864.587000ms +Mean Cycle Time: 0.778646ms (± 1.020442ms) +Cycles Per Second: 1284.280876 +GC: off + +Note: Pre-advisory lock implementation + +ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0156.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Get Benchmark - 2018-06-05T02-27-02.253Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt b/test/benchmark/results/Get Benchmark - 2018-06-05T02-27-02.253Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt new file mode 100644 index 0000000..4c05dfa --- /dev/null +++ b/test/benchmark/results/Get Benchmark - 2018-06-05T02-27-02.253Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt @@ -0,0 +1,23 @@ +Get Benchmark +- - - +Cycles: 100000 +Time: 89734.621000ms +Mean Cycle Time: 0.897346ms (± 2.752222ms) +Cycles Per Second: 1114.397084 +GC: off + +Note: Post-advisory lock implementation + +ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0156.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Get Benchmark - 2018-06-08T06-42-47.108Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt b/test/benchmark/results/Get Benchmark - 2018-06-08T06-42-47.108Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt new file mode 100644 index 0000000..efb8d67 --- /dev/null +++ b/test/benchmark/results/Get Benchmark - 2018-06-08T06-42-47.108Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt @@ -0,0 +1,23 @@ +Get Benchmark +- - - +Cycles: 100000 +Time: 81638.440000ms +Mean Cycle Time: 0.816384ms (± 0.346983ms) +Cycles Per Second: 1224.913166 +GC: off + +Note: Post-retrieval server function implementation + +ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0156.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Get Benchmark - 2018-10-29T17-46-05.986Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt b/test/benchmark/results/Get Benchmark - 2018-10-29T17-46-05.986Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt new file mode 100644 index 0000000..258fe95 --- /dev/null +++ b/test/benchmark/results/Get Benchmark - 2018-10-29T17-46-05.986Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt @@ -0,0 +1,23 @@ +Get Benchmark +- - - +Cycles: 100000 +Time: 86420.050000ms +Mean Cycle Time: 0.864200ms (± 1.574568ms) +Cycles Per Second: 1157.138882 +GC: off + +Note: Pre-change of varchar(n) columns to text columns + +ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Get Benchmark - 2018-10-29T18-06-45.490Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt b/test/benchmark/results/Get Benchmark - 2018-10-29T18-06-45.490Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt new file mode 100644 index 0000000..cda3fff --- /dev/null +++ b/test/benchmark/results/Get Benchmark - 2018-10-29T18-06-45.490Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt @@ -0,0 +1,23 @@ +Get Benchmark +- - - +Cycles: 100000 +Time: 86997.039000ms +Mean Cycle Time: 0.869970ms (± 1.669369ms) +Cycles Per Second: 1149.464409 +GC: off + +Note: Post-change of varchar(n) columns to text columns + +ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Get Benchmark - 2019-02-06T01-21-21.133Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt b/test/benchmark/results/Get Benchmark - 2019-02-06T01-21-21.133Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt new file mode 100644 index 0000000..f6db09a --- /dev/null +++ b/test/benchmark/results/Get Benchmark - 2019-02-06T01-21-21.133Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt @@ -0,0 +1,21 @@ +Get Benchmark +- - - +Cycles: 100000 +Time: 79444.591000ms +Mean Cycle Time: 0.794446ms (± 0.603666ms) +Cycles Per Second: 1258.738937 +GC: off + +ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Get Benchmark - 2019-02-06T02-29-48.515Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt b/test/benchmark/results/Get Benchmark - 2019-02-06T02-29-48.515Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt new file mode 100644 index 0000000..4e459a2 --- /dev/null +++ b/test/benchmark/results/Get Benchmark - 2019-02-06T02-29-48.515Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt @@ -0,0 +1,21 @@ +Get Benchmark +- - - +Cycles: 100000 +Time: 62712.511000ms +Mean Cycle Time: 0.627125ms (± 0.081911ms) +Cycles Per Second: 1594.578154 +GC: off + +ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Get Benchmark - 2019-02-06T03-28-10.452Z - truffleruby 1.0.0-rc12, like ruby 2.4.4, GraalVM CE Native [x86_64-darwin].txt b/test/benchmark/results/Get Benchmark - 2019-02-06T03-28-10.452Z - truffleruby 1.0.0-rc12, like ruby 2.4.4, GraalVM CE Native [x86_64-darwin].txt new file mode 100644 index 0000000..632b197 --- /dev/null +++ b/test/benchmark/results/Get Benchmark - 2019-02-06T03-28-10.452Z - truffleruby 1.0.0-rc12, like ruby 2.4.4, GraalVM CE Native [x86_64-darwin].txt @@ -0,0 +1,21 @@ +Get Benchmark +- - - +Cycles: 100000 +Time: 196650.188980ms +Mean Cycle Time: 1.966502ms (± 23.192495ms) +Cycles Per Second: 508.517182 +GC: off + +truffleruby 1.0.0-rc12, like ruby 2.4.4, GraalVM CE Native [x86_64-darwin] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Get Benchmark - 2019-02-06T04-01-19.164Z - truffleruby 1.0.0-rc12, like ruby 2.4.4, GraalVM CE Native [x86_64-darwin].txt b/test/benchmark/results/Get Benchmark - 2019-02-06T04-01-19.164Z - truffleruby 1.0.0-rc12, like ruby 2.4.4, GraalVM CE Native [x86_64-darwin].txt new file mode 100644 index 0000000..4dbb9b8 --- /dev/null +++ b/test/benchmark/results/Get Benchmark - 2019-02-06T04-01-19.164Z - truffleruby 1.0.0-rc12, like ruby 2.4.4, GraalVM CE Native [x86_64-darwin].txt @@ -0,0 +1,21 @@ +Get Benchmark +- - - +Cycles: 100000 +Time: 207754.828951ms +Mean Cycle Time: 2.077548ms (± 24.335594ms) +Cycles Per Second: 481.336586 +GC: on + +truffleruby 1.0.0-rc12, like ruby 2.4.4, GraalVM CE Native [x86_64-darwin] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Get Benchmark - 2019-09-12T20-34-51.223Z - truffleruby 19.2.0, like ruby 2.6.2, GraalVM CE Native [x86_64-darwin].txt b/test/benchmark/results/Get Benchmark - 2019-09-12T20-34-51.223Z - truffleruby 19.2.0, like ruby 2.6.2, GraalVM CE Native [x86_64-darwin].txt new file mode 100644 index 0000000..f7e98e6 --- /dev/null +++ b/test/benchmark/results/Get Benchmark - 2019-09-12T20-34-51.223Z - truffleruby 19.2.0, like ruby 2.6.2, GraalVM CE Native [x86_64-darwin].txt @@ -0,0 +1,21 @@ +Get Benchmark +- - - +Cycles: 100000 +Time: 195429.244053ms +Mean Cycle Time: 1.954292ms (± 25.676337ms) +Cycles Per Second: 511.694145 +GC: off + +truffleruby 19.2.0, like ruby 2.6.2, GraalVM CE Native [x86_64-darwin] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Get Benchmark - 2019-11-06T08-49-54.515Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt b/test/benchmark/results/Get Benchmark - 2019-11-06T08-49-54.515Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt new file mode 100644 index 0000000..d22ea2e --- /dev/null +++ b/test/benchmark/results/Get Benchmark - 2019-11-06T08-49-54.515Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt @@ -0,0 +1,23 @@ +Get Benchmark +- - - +Cycles: 100000 +Time: 61260.112000ms +Mean Cycle Time: 0.612601ms (± 0.939958ms) +Cycles Per Second: 1632.383565 +GC: off + +Note: After implementation of correlation in the message store + +ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Get Category Benchmark - 2019-12-19T19-45-12.412Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt b/test/benchmark/results/Get Category Benchmark - 2019-12-19T19-45-12.412Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt new file mode 100644 index 0000000..251c86b --- /dev/null +++ b/test/benchmark/results/Get Category Benchmark - 2019-12-19T19-45-12.412Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt @@ -0,0 +1,23 @@ +Get Category Benchmark +- - - +Cycles: 100000 +Time: 97735.090000ms +Mean Cycle Time: 0.977351ms (± 0.619131ms) +Cycles Per Second: 1023.173970 +GC: off + +Note: After after stream and category get benchmarks are separated + +ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Get Category Benchmark - 2020-11-20T01-25-27.278Z - ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin19].txt b/test/benchmark/results/Get Category Benchmark - 2020-11-20T01-25-27.278Z - ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin19].txt new file mode 100644 index 0000000..706f826 --- /dev/null +++ b/test/benchmark/results/Get Category Benchmark - 2020-11-20T01-25-27.278Z - ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin19].txt @@ -0,0 +1,21 @@ +Get Category Benchmark +- - - +Cycles: 100000 +Time: 73931.646000ms +Mean Cycle Time: 0.739316ms (± 0.249903ms) +Cycles Per Second: 1352.600752 +GC: off + +ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin19] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Get Stream Benchmark - 2019-12-19T19-50-34.958Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt b/test/benchmark/results/Get Stream Benchmark - 2019-12-19T19-50-34.958Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt new file mode 100644 index 0000000..4f371d4 --- /dev/null +++ b/test/benchmark/results/Get Stream Benchmark - 2019-12-19T19-50-34.958Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt @@ -0,0 +1,23 @@ +Get Stream Benchmark +- - - +Cycles: 100000 +Time: 90234.657000ms +Mean Cycle Time: 0.902347ms (± 0.656719ms) +Cycles Per Second: 1108.221645 +GC: off + +Note: After after stream and category get benchmarks are separated + +ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Get Stream Benchmark - 2020-11-20T03-10-44.280Z - ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin19].txt b/test/benchmark/results/Get Stream Benchmark - 2020-11-20T03-10-44.280Z - ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin19].txt new file mode 100644 index 0000000..8786685 --- /dev/null +++ b/test/benchmark/results/Get Stream Benchmark - 2020-11-20T03-10-44.280Z - ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin19].txt @@ -0,0 +1,21 @@ +Get Stream Benchmark +- - - +Cycles: 100000 +Time: 70176.616000ms +Mean Cycle Time: 0.701766ms (± 0.201194ms) +Cycles Per Second: 1424.976092 +GC: off + +ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin19] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Put Benchmark - 2018-06-05T01-59-19.666Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt b/test/benchmark/results/Put Benchmark - 2018-06-05T01-59-19.666Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt new file mode 100644 index 0000000..f28411b --- /dev/null +++ b/test/benchmark/results/Put Benchmark - 2018-06-05T01-59-19.666Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt @@ -0,0 +1,23 @@ +Put Benchmark +- - - +Cycles: 100000 +Time: 137845.491000ms +Mean Cycle Time: 1.378455ms (± 1.311459ms) +Cycles Per Second: 725.449917 +GC: off + +Note: Pre-advisory lock implementation + +ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0156.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Put Benchmark - 2018-06-05T02-14-58.644Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt b/test/benchmark/results/Put Benchmark - 2018-06-05T02-14-58.644Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt new file mode 100644 index 0000000..4e5440f --- /dev/null +++ b/test/benchmark/results/Put Benchmark - 2018-06-05T02-14-58.644Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt @@ -0,0 +1,24 @@ +Put Benchmark +- - - +Cycles: 100000 +Time: 146689.646000ms +Mean Cycle Time: 1.466896ms (± 1.990456ms) +Cycles Per Second: 681.711373 +GC: off + +Note: Post-advisory lock implementation + +ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0156.B00 +SMC Version (system): 2.42f10 + diff --git a/test/benchmark/results/Put Benchmark - 2018-06-25T15-41-28.825Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt b/test/benchmark/results/Put Benchmark - 2018-06-25T15-41-28.825Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt new file mode 100644 index 0000000..866cc0d --- /dev/null +++ b/test/benchmark/results/Put Benchmark - 2018-06-25T15-41-28.825Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt @@ -0,0 +1,23 @@ +Put Benchmark +- - - +Cycles: 100000 +Time: 127165.967000ms +Mean Cycle Time: 1.271660ms (± 0.604510ms) +Cycles Per Second: 786.373920 +GC: off + +Note: ID index has a unique constraint + +ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0156.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Put Benchmark - 2018-10-29T16-49-53.149Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt b/test/benchmark/results/Put Benchmark - 2018-10-29T16-49-53.149Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt new file mode 100644 index 0000000..80a388f --- /dev/null +++ b/test/benchmark/results/Put Benchmark - 2018-10-29T16-49-53.149Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt @@ -0,0 +1,23 @@ +Put Benchmark +- - - +Cycles: 100000 +Time: 136078.335000ms +Mean Cycle Time: 1.360783ms (± 0.736631ms) +Cycles Per Second: 734.870837 +GC: off + +Note: Pre-change of varchar(n) columns to text columns + +ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Put Benchmark - 2018-10-29T18-01-54.527Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt b/test/benchmark/results/Put Benchmark - 2018-10-29T18-01-54.527Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt new file mode 100644 index 0000000..58084bd --- /dev/null +++ b/test/benchmark/results/Put Benchmark - 2018-10-29T18-01-54.527Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt @@ -0,0 +1,23 @@ +Put Benchmark +- - - +Cycles: 100000 +Time: 144978.105000ms +Mean Cycle Time: 1.449781ms (± 1.533522ms) +Cycles Per Second: 689.759326 +GC: off + +Note: Post-change of varchar(n) columns to text columns + +ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Put Benchmark - 2019-02-06T01-13-04.354Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt b/test/benchmark/results/Put Benchmark - 2019-02-06T01-13-04.354Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt new file mode 100644 index 0000000..761ea02 --- /dev/null +++ b/test/benchmark/results/Put Benchmark - 2019-02-06T01-13-04.354Z - ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17].txt @@ -0,0 +1,21 @@ +Put Benchmark +- - - +Cycles: 100000 +Time: 145874.364000ms +Mean Cycle Time: 1.458744ms (± 4.047611ms) +Cycles Per Second: 685.521412 +GC: off + +ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Put Benchmark - 2019-02-06T02-33-29.071Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt b/test/benchmark/results/Put Benchmark - 2019-02-06T02-33-29.071Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt new file mode 100644 index 0000000..c727fa3 --- /dev/null +++ b/test/benchmark/results/Put Benchmark - 2019-02-06T02-33-29.071Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt @@ -0,0 +1,21 @@ +Put Benchmark +- - - +Cycles: 100000 +Time: 137535.101000ms +Mean Cycle Time: 1.375351ms (± 0.955491ms) +Cycles Per Second: 727.087116 +GC: off + +ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Put Benchmark - 2019-02-06T03-07-01.152Z - truffleruby 1.0.0-rc12, like ruby 2.4.4, GraalVM CE Native [x86_64-darwin].txt b/test/benchmark/results/Put Benchmark - 2019-02-06T03-07-01.152Z - truffleruby 1.0.0-rc12, like ruby 2.4.4, GraalVM CE Native [x86_64-darwin].txt new file mode 100644 index 0000000..3244702 --- /dev/null +++ b/test/benchmark/results/Put Benchmark - 2019-02-06T03-07-01.152Z - truffleruby 1.0.0-rc12, like ruby 2.4.4, GraalVM CE Native [x86_64-darwin].txt @@ -0,0 +1,21 @@ +Put Benchmark +- - - +Cycles: 100000 +Time: 187106.754654ms +Mean Cycle Time: 1.871068ms (± 15.991498ms) +Cycles Per Second: 534.454249 +GC: off + +truffleruby 1.0.0-rc12, like ruby 2.4.4, GraalVM CE Native [x86_64-darwin] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Put Benchmark - 2019-02-06T03-52-34.879Z - truffleruby 1.0.0-rc12, like ruby 2.4.4, GraalVM CE Native [x86_64-darwin].txt b/test/benchmark/results/Put Benchmark - 2019-02-06T03-52-34.879Z - truffleruby 1.0.0-rc12, like ruby 2.4.4, GraalVM CE Native [x86_64-darwin].txt new file mode 100644 index 0000000..a07db7a --- /dev/null +++ b/test/benchmark/results/Put Benchmark - 2019-02-06T03-52-34.879Z - truffleruby 1.0.0-rc12, like ruby 2.4.4, GraalVM CE Native [x86_64-darwin].txt @@ -0,0 +1,21 @@ +Put Benchmark +- - - +Cycles: 100000 +Time: 199665.528250ms +Mean Cycle Time: 1.996655ms (± 17.571019ms) +Cycles Per Second: 500.837580 +GC: on + +truffleruby 1.0.0-rc12, like ruby 2.4.4, GraalVM CE Native [x86_64-darwin] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Put Benchmark - 2019-02-21T04-36-28.780Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt b/test/benchmark/results/Put Benchmark - 2019-02-21T04-36-28.780Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt new file mode 100644 index 0000000..b7c52c3 --- /dev/null +++ b/test/benchmark/results/Put Benchmark - 2019-02-21T04-36-28.780Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt @@ -0,0 +1,23 @@ +Put Benchmark +- - - +Cycles: 100000 +Time: 141589.302000ms +Mean Cycle Time: 1.415893ms (± 1.068947ms) +Cycles Per Second: 706.268048 +GC: off + +Note: Post category advisory lock + +ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Put Benchmark - 2019-02-21T05-11-28.525Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt b/test/benchmark/results/Put Benchmark - 2019-02-21T05-11-28.525Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt new file mode 100644 index 0000000..191b501 --- /dev/null +++ b/test/benchmark/results/Put Benchmark - 2019-02-21T05-11-28.525Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt @@ -0,0 +1,23 @@ +Put Benchmark +- - - +Cycles: 100000 +Time: 306687.095000ms +Mean Cycle Time: 3.066871ms (± 3.746017ms) +Cycles Per Second: 326.065236 +GC: off + +Note: Tested with 5 concurrent writers on the same category + +ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Put Benchmark - 2019-09-12T16-54-47.909Z - truffleruby 19.2.0, like ruby 2.6.2, GraalVM CE Native [x86_64-darwin].txt b/test/benchmark/results/Put Benchmark - 2019-09-12T16-54-47.909Z - truffleruby 19.2.0, like ruby 2.6.2, GraalVM CE Native [x86_64-darwin].txt new file mode 100644 index 0000000..5ff7894 --- /dev/null +++ b/test/benchmark/results/Put Benchmark - 2019-09-12T16-54-47.909Z - truffleruby 19.2.0, like ruby 2.6.2, GraalVM CE Native [x86_64-darwin].txt @@ -0,0 +1,21 @@ +Put Benchmark +- - - +Cycles: 100000 +Time: 288567.912765ms +Mean Cycle Time: 2.885679ms (± 18.626377ms) +Cycles Per Second: 346.538876 +GC: off + +truffleruby 19.2.0, like ruby 2.6.2, GraalVM CE Native [x86_64-darwin] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Put Benchmark - 2019-11-06T07-53-35.642Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt b/test/benchmark/results/Put Benchmark - 2019-11-06T07-53-35.642Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt new file mode 100644 index 0000000..1830a6a --- /dev/null +++ b/test/benchmark/results/Put Benchmark - 2019-11-06T07-53-35.642Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt @@ -0,0 +1,23 @@ +Put Benchmark +- - - +Cycles: 100000 +Time: 139714.202000ms +Mean Cycle Time: 1.397142ms (± 0.755262ms) +Cycles Per Second: 715.746850 +GC: off + +Note: After implementation of correlation in the message store + +ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Put Benchmark - 2019-12-19T20-11-52.599Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt b/test/benchmark/results/Put Benchmark - 2019-12-19T20-11-52.599Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt new file mode 100644 index 0000000..5f17994 --- /dev/null +++ b/test/benchmark/results/Put Benchmark - 2019-12-19T20-11-52.599Z - ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17].txt @@ -0,0 +1,23 @@ +Put Benchmark +- - - +Cycles: 100000 +Time: 144316.699000ms +Mean Cycle Time: 1.443167ms (± 0.909076ms) +Cycles Per Second: 692.920505 +GC: off + +Note: After after stream and category get benchmarks are separated + +ruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin17] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/benchmark/results/Put Benchmark - 2020-11-20T01-14-19.439Z - ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin19].txt b/test/benchmark/results/Put Benchmark - 2020-11-20T01-14-19.439Z - ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin19].txt new file mode 100644 index 0000000..85308ad --- /dev/null +++ b/test/benchmark/results/Put Benchmark - 2020-11-20T01-14-19.439Z - ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin19].txt @@ -0,0 +1,21 @@ +Put Benchmark +- - - +Cycles: 100000 +Time: 140985.021000ms +Mean Cycle Time: 1.409850ms (± 0.395411ms) +Cycles Per Second: 709.295209 +GC: off + +ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin19] + +Model Name: MacBook +Model Identifier: MacBook10,1 +Processor Name: Intel Core i7 +Processor Speed: 1.4 GHz +Number of Processors: 1 +Total Number of Cores: 2 +L2 Cache (per Core): 256 KB +L3 Cache: 4 MB +Memory: 16 GB +Boot ROM Version: MB101.0163.B00 +SMC Version (system): 2.42f10 diff --git a/test/interactive/concurrency/concurrency_init.rb b/test/interactive/concurrency/concurrency_init.rb new file mode 100644 index 0000000..89303b6 --- /dev/null +++ b/test/interactive/concurrency/concurrency_init.rb @@ -0,0 +1,5 @@ +ENV['LOG_TAGS'] ||= 'actor' + +require_relative '../interactive_init' + +require_relative 'defaults' diff --git a/test/interactive/concurrency/defaults.rb b/test/interactive/concurrency/defaults.rb new file mode 100644 index 0000000..6e6e9dd --- /dev/null +++ b/test/interactive/concurrency/defaults.rb @@ -0,0 +1,11 @@ +module Test + module Interactive + module Concurrency + class Defaults + def self.actors + Integer(ENV['ACTORS'] || 2) + end + end + end + end +end diff --git a/test/interactive/concurrency/write_to_single_stream.rb b/test/interactive/concurrency/write_to_single_stream.rb new file mode 100644 index 0000000..4ec3514 --- /dev/null +++ b/test/interactive/concurrency/write_to_single_stream.rb @@ -0,0 +1,52 @@ +require_relative 'concurrency_init' + +require 'actor' + +module Test + module Interactive + module Concurrency + class Write + include Actor + include Log::Dependency + + attr_reader :stream_name + + def initialize(stream_name) + @stream_name = stream_name + end + + handle :start do + :write_message + end + + handle :write_message do + message_data_1 = MessageStore::Controls::MessageData::Write.example(data: { actor: object_id }) + message_data_2 = MessageStore::Controls::MessageData::Write.example(data: { actor: object_id }) + batch = [message_data_1, message_data_2] + + position = MessageStore::Write.(batch, stream_name) + + logger.info(tag: :actor) { "Wrote message data (Object ID: #{object_id}, Position: #{position}, Message Type: #{message_data_1.type.inspect}, Stream Name: #{stream_name.inspect})" } + + :write_message + end + end + end + end +end + +random = SecureRandom.hex[0..3] +stream_name = Controls::StreamName.example(category: "testConcurrentWrite_#{random}", randomize_category: false, id: :none) + +number_of_actors = Test::Interactive::Concurrency::Defaults.actors + +puts +puts "Concurrent Write (#{number_of_actors} actors)" +puts "- - -" +puts + +Actor::Supervisor.start do + number_of_actors.times do + Test::Interactive::Concurrency::Write.start(stream_name) + end +end diff --git a/test/interactive/interactive_init.rb b/test/interactive/interactive_init.rb new file mode 100644 index 0000000..0893a61 --- /dev/null +++ b/test/interactive/interactive_init.rb @@ -0,0 +1,4 @@ +ENV['LOG_LEVEL'] ||= 'info' +ENV['LOG_TAGS'] ||= '_untagged,message_store_postgres,-data' + +require_relative '../test_init' diff --git a/test/package/package.sh b/test/package/package.sh new file mode 100755 index 0000000..156475e --- /dev/null +++ b/test/package/package.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +set -ue + +rm -f *.gem +rm -rf test/package/installed + +for gemspec in *.gemspec; do + echo "Building $gemspec" + gem build $gemspec --norc +done + +for gem in *.gem; do + echo "Installing $gem" + gem install $gem \ + --install-dir ./test/package/installed \ + --norc \ + --no-document \ + --no-ri +done + +GEM_PATH=test/package/installed test/package/installed/bin/evt-pg-delete-db +GEM_PATH=test/package/installed test/package/installed/bin/evt-pg-create-db +GEM_PATH=test/package/installed test/package/installed/bin/evt-pg-recreate-db diff --git a/test/test_init.rb b/test/test_init.rb index a895fe5..55634d1 100644 --- a/test/test_init.rb +++ b/test/test_init.rb @@ -1,21 +1,11 @@ ENV['CONSOLE_DEVICE'] ||= 'stdout' -ENV['LOG_COLOR'] ||= 'on' - -if ENV['LOG_LEVEL'] - ENV['LOGGER'] ||= 'on' -else - ENV['LOG_LEVEL'] ||= 'trace' -end - -ENV['LOGGER'] ||= 'off' -ENV['LOG_OPTIONAL'] ||= 'on' +ENV['LOG_LEVEL'] ||= '_min' puts RUBY_DESCRIPTION -require_relative '../init.rb' +require_relative '../init' +require 'message_store/controls' require 'test_bench'; TestBench.activate -require 'message_store/controls' - include MessageStore diff --git a/tools/write_message.rb b/tools/write_message.rb new file mode 100755 index 0000000..0c7d6ca --- /dev/null +++ b/tools/write_message.rb @@ -0,0 +1,26 @@ +#!/usr/bin/env ruby + +ENV['CONSOLE_DEVICE'] ||= 'stdout' +ENV['LOG_LEVEL'] ||= '_min' + +puts RUBY_DESCRIPTION + +require_relative '../init' + +require 'message_store/controls' + +include MessageStore + +instances = Integer(ENV['INSTANCES'] || 1) +stream_name = ENV['STREAM_NAME'] + +puts +puts "Writing #{instances} messages" +puts "- - -" + +instances.times do |i| + stream_name, position = Controls::Put.(stream_name: stream_name) + puts "Instance: #{i}, Position: #{position}, Stream Name: #{stream_name}" +end + +puts