From 16da29309cdb117e2ac40eb93286fc265c3135d5 Mon Sep 17 00:00:00 2001 From: Azul Date: Wed, 23 Mar 2016 21:53:29 +0100 Subject: vendoring couchrest session store for development 0.4.0 has not been released yet. But travis needs it to run. So i vendor it for now. Will remove it again when the build is getting stable. --- vendor/gems/couchrest_session_store/.ruby-version | 1 + vendor/gems/couchrest_session_store/.travis.yml | 6 + vendor/gems/couchrest_session_store/Gemfile | 7 ++ vendor/gems/couchrest_session_store/README.md | 133 +++++++++++++++++++++ vendor/gems/couchrest_session_store/Rakefile | 9 ++ .../couchrest_session_store.gemspec | 25 ++++ .../couchrest_session_store/design/Session.json | 8 ++ .../lib/couchrest/session.rb | 15 +++ .../lib/couchrest/session/utility.rb | 12 ++ .../lib/couchrest_session_store.rb | 11 ++ .../couchrest_session_store/test/couch_tester.rb | 27 +++++ .../test/database_rotation_test.rb | 88 ++++++++++++++ .../test/session_document_test.rb | 27 +++++ .../couchrest_session_store/test/setup_couch.sh | 7 ++ .../couchrest_session_store/test/stress_test.rb | 51 ++++++++ .../couchrest_session_store/test/test_clock.rb | 12 ++ .../couchrest_session_store/test/test_helper.rb | 9 ++ 17 files changed, 448 insertions(+) create mode 100644 vendor/gems/couchrest_session_store/.ruby-version create mode 100644 vendor/gems/couchrest_session_store/.travis.yml create mode 100644 vendor/gems/couchrest_session_store/Gemfile create mode 100644 vendor/gems/couchrest_session_store/README.md create mode 100644 vendor/gems/couchrest_session_store/Rakefile create mode 100644 vendor/gems/couchrest_session_store/couchrest_session_store.gemspec create mode 100644 vendor/gems/couchrest_session_store/design/Session.json create mode 100644 vendor/gems/couchrest_session_store/lib/couchrest/session.rb create mode 100644 vendor/gems/couchrest_session_store/lib/couchrest/session/utility.rb create mode 100644 vendor/gems/couchrest_session_store/lib/couchrest_session_store.rb create mode 100644 vendor/gems/couchrest_session_store/test/couch_tester.rb create mode 100644 vendor/gems/couchrest_session_store/test/database_rotation_test.rb create mode 100644 vendor/gems/couchrest_session_store/test/session_document_test.rb create mode 100755 vendor/gems/couchrest_session_store/test/setup_couch.sh create mode 100644 vendor/gems/couchrest_session_store/test/stress_test.rb create mode 100644 vendor/gems/couchrest_session_store/test/test_clock.rb create mode 100644 vendor/gems/couchrest_session_store/test/test_helper.rb (limited to 'vendor') diff --git a/vendor/gems/couchrest_session_store/.ruby-version b/vendor/gems/couchrest_session_store/.ruby-version new file mode 100644 index 0000000..f3a9c9a --- /dev/null +++ b/vendor/gems/couchrest_session_store/.ruby-version @@ -0,0 +1 @@ +1.9.3-p194 diff --git a/vendor/gems/couchrest_session_store/.travis.yml b/vendor/gems/couchrest_session_store/.travis.yml new file mode 100644 index 0000000..073d421 --- /dev/null +++ b/vendor/gems/couchrest_session_store/.travis.yml @@ -0,0 +1,6 @@ +services: + - couchdb +notifications: + email: false +before_script: + - "test/setup_couch.sh" diff --git a/vendor/gems/couchrest_session_store/Gemfile b/vendor/gems/couchrest_session_store/Gemfile new file mode 100644 index 0000000..88b81a0 --- /dev/null +++ b/vendor/gems/couchrest_session_store/Gemfile @@ -0,0 +1,7 @@ +source 'https://rubygems.org' + +# Specify your gem's dependencies in couchrest-session-store.gemspec +gemspec + +gem 'debugger' + diff --git a/vendor/gems/couchrest_session_store/README.md b/vendor/gems/couchrest_session_store/README.md new file mode 100644 index 0000000..60e692e --- /dev/null +++ b/vendor/gems/couchrest_session_store/README.md @@ -0,0 +1,133 @@ +# CouchRest::Session::Store # + +A simple session store based on CouchRest Model. + +## Setup ## + +`CouchRest::Session::Store` will automatically pick up the config/couch.yml +file used by CouchRest Model. + +Cleaning up sessions requires a design document in the sessions database that +enables querying by expiry. See `design/Session.json` for an example. This +design document is loaded for tests, but you will need to load it on your own +in a production environment. For example: + + curl -X PUT username:password@localhost:5984/couchrest_sessions/_design/Session --data @design/Session.json + +## Options ## + +* marshal_data: (_defaults true_) - if set to false session data will be stored + directly in the couch document. Otherwise it's marshalled and base64 encoded + to enable restoring ruby data structures. +* database: database to use combined with config prefix and suffix +* expire_after: lifetime of a session in seconds. + +## Dynamic Databases ## + +This gem also includes the module `CouchRest::Model::DatabaseMethod`, which +allow a Model to dynamically choose what database to use. + +An example of specifying database dynamically: + + class Token < CouchRest::Model::Base + include CouchRest::Model::DatabaseMethod + + use_database_method :database_name + + def self.database_name + time = Time.now.utc + "tokens_#{time.year}_#{time.month}" + end + end + +A couple notes: + +Once you include `CouchRest::Model::DatabaseMethod`, the database is no longer +automatically created. In this example, you would need to run +`Token.database.create!` or `Token.database!` in order to create the database. + +The symbol passed to `database_method` must match the name of a class method, +but if there is also an instance method with the same name then this instance +method will be called when appropriate. To state the obvious, tread lightly: +there be dragons when generating database names that depend on properties of +the instance. + +## Database Rotation ## + +The module `CouchRest::Model::Rotation` can be included in a Model in +order to use dynamic databases to perform database rotation. + +CouchDB is not good for ephemeral data because old documents are never really +deleted: when you deleted a document, it just appends a new revision. The bulk +of the old data is not stored, but it does store a record for each doc id and +revision id for the document. In the case of ephemeral data, like tokens, +sessions, or statistics, this will quickly bloat the database with a ton of +useless deleted documents. The proper solution is to rotate the databases: +create a new one regularly and delete the old one entirely. This will allow +you to recover the storage space. + +A better solution might be to just use a different database for all +ephemeral data, like MariaDB or Redis. But, if you really want to use CouchDB, this +is how you can do it. + +An example of specifying database rotation: + + class Token < CouchRest::Model::Base + include CouchRest::Model::Rotation + + rotate_database 'tokens', :every => 30.days + end + +Then, in a task triggered by a cron job: + + CouchRest::Model::Base.configure do |conf| + conf.environment = Rails.env + conf.connection_config_file = File.join(Rails.root, 'config', 'couchdb.admin.yml') + end + Token.rotate_database_now(:window => 1.day) + +Or perhaps: + + Rails.application.eager_load! + CouchRest::Model::Rotation.descendants.each do |model| + model.rotate_database_now + end + +The `:window` argument to `rotate_database_now` specifies how far in advance we +should create the new database (default 1.day). For ideal behavior, this value +should be GREATER than or equal to the frequency with which the cron job is +run. For example, if the cron job is run every hour, the argument can be +`1.hour`, `2.hours`, `1.day`, but not `20.minutes`. + +The method `rotate_database_now` will do nothing if the database has already +been rotated. Otherwise, as needed, it will create the new database, create +the design documents, set up replication between the old and new databases, +and delete the old database (once it is not used anymore). + +These actions will require admin access, so if your application normally runs +without admin rights you will need specify a different configuration for +CouchRest::Model before `rotate_database_now` is called. + +Known issues: + +* If you change the rotation period, there will be a break in the rotation + (old documents will not get replicated to the new rotated db) and the old db + will not get automatically deleted. + +* Calling `Model.database.delete!` will not necessarily remove all the + relevant databases because of the way prior and future databases are kept + for the 'window' period. + +## Changes ## + +0.3.0 + +* Added support for dynamic and rotating databases. + +0.2.4 + +* Do not crash if can't connect to CouchDB + +0.2.3 + +* Better retry and conflict catching.d \ No newline at end of file diff --git a/vendor/gems/couchrest_session_store/Rakefile b/vendor/gems/couchrest_session_store/Rakefile new file mode 100644 index 0000000..8e1dc9c --- /dev/null +++ b/vendor/gems/couchrest_session_store/Rakefile @@ -0,0 +1,9 @@ +#!/usr/bin/env rake +require "bundler/gem_tasks" +require 'rake/testtask' + +task default: [:test] + +Rake::TestTask.new do |t| + t.pattern = "test/*_test.rb" +end diff --git a/vendor/gems/couchrest_session_store/couchrest_session_store.gemspec b/vendor/gems/couchrest_session_store/couchrest_session_store.gemspec new file mode 100644 index 0000000..bf3b2b2 --- /dev/null +++ b/vendor/gems/couchrest_session_store/couchrest_session_store.gemspec @@ -0,0 +1,25 @@ +# _*_ encoding: utf-8 -*- + +Gem::Specification.new do |gem| + + gem.authors = ["Azul"] + gem.email = ["azul@leap.se"] + gem.summary = "A Rails Session Store based on CouchRest Model" + gem.description = gem.summary + gem.homepage = "http://github.com/azul/couchrest_session_store" + + gem.has_rdoc = true +# gem.extra_rdoc_files = ["LICENSE"] + + gem.files = `git ls-files`.split("\n") + gem.name = "couchrest_session_store" + gem.require_paths = ["lib"] + gem.version = '0.4.0' + + gem.add_dependency "couchrest" + gem.add_dependency "couchrest_model" + gem.add_dependency "actionpack", '~> 4.0' + + gem.add_development_dependency "minitest" + gem.add_development_dependency "rake" +end diff --git a/vendor/gems/couchrest_session_store/design/Session.json b/vendor/gems/couchrest_session_store/design/Session.json new file mode 100644 index 0000000..7020278 --- /dev/null +++ b/vendor/gems/couchrest_session_store/design/Session.json @@ -0,0 +1,8 @@ +{ + "views": { + "by_expires": { + "reduce": "_sum", + "map": "function(doc) {\n if(typeof doc.expires !== \"undefined\") {\n emit(doc.expires, 1);\n }\n}\n" + } + } +} diff --git a/vendor/gems/couchrest_session_store/lib/couchrest/session.rb b/vendor/gems/couchrest_session_store/lib/couchrest/session.rb new file mode 100644 index 0000000..430732c --- /dev/null +++ b/vendor/gems/couchrest_session_store/lib/couchrest/session.rb @@ -0,0 +1,15 @@ +module CouchRest + + class StorageMissing < Exception + attr_reader :db + def initialize(request, db) + super(request) + @db = db.name + @message = "The database '#{db}' does not exist." + end + end + + module Session + end +end + diff --git a/vendor/gems/couchrest_session_store/lib/couchrest/session/utility.rb b/vendor/gems/couchrest_session_store/lib/couchrest/session/utility.rb new file mode 100644 index 0000000..3982c28 --- /dev/null +++ b/vendor/gems/couchrest_session_store/lib/couchrest/session/utility.rb @@ -0,0 +1,12 @@ +module CouchRest::Session::Utility + module_function + + def marshal(data) + ::Base64.encode64(Marshal.dump(data)) if data + end + + def unmarshal(data) + Marshal.load(::Base64.decode64(data)) if data + end + +end diff --git a/vendor/gems/couchrest_session_store/lib/couchrest_session_store.rb b/vendor/gems/couchrest_session_store/lib/couchrest_session_store.rb new file mode 100644 index 0000000..78c6a25 --- /dev/null +++ b/vendor/gems/couchrest_session_store/lib/couchrest_session_store.rb @@ -0,0 +1,11 @@ +require 'couchrest' +require 'couchrest_model' +# ensure compatibility with couchrest_model +gem 'actionpack', '~> 4.0' +require 'action_dispatch' + +require 'couchrest/model/database_method' +require 'couchrest/model/rotation' +require 'couchrest/session' +require 'couchrest/session/store' +require 'couchrest/session/document' diff --git a/vendor/gems/couchrest_session_store/test/couch_tester.rb b/vendor/gems/couchrest_session_store/test/couch_tester.rb new file mode 100644 index 0000000..b623044 --- /dev/null +++ b/vendor/gems/couchrest_session_store/test/couch_tester.rb @@ -0,0 +1,27 @@ +# +# Access the couch directly so we can test its state without relying +# on the SessionStore +# + +class CouchTester < CouchRest::Document + include CouchRest::Model::Configuration + include CouchRest::Model::Connection + include CouchRest::Model::Rotation + + rotate_database 'sessions', + :every => 1.month, :expiration_field => :expires + + def initialize(options = {}) + end + + def get(sid) + database.get(sid) + end + + def update(sid, diff) + doc = database.get(sid) + doc.merge! diff + database.save_doc(doc) + end + +end diff --git a/vendor/gems/couchrest_session_store/test/database_rotation_test.rb b/vendor/gems/couchrest_session_store/test/database_rotation_test.rb new file mode 100644 index 0000000..856db11 --- /dev/null +++ b/vendor/gems/couchrest_session_store/test/database_rotation_test.rb @@ -0,0 +1,88 @@ +require_relative 'test_helper' + +class RotationTest < MiniTest::Test + + class Token < CouchRest::Model::Base + include CouchRest::Model::Rotation + property :token, String + rotate_database 'test_rotate', :every => 1.day + end + + TEST_DB_RE = /test_rotate_\d+/ + + def test_rotate + delete_all_dbs + doc = nil + original_name = nil + next_db_name = nil + + Time.stub :now, Time.gm(2015,3,7,0) do + Token.create_database! + doc = Token.create!(:token => 'aaaa') + original_name = Token.rotated_database_name + assert database_exists?(original_name) + assert_equal 1, count_dbs + end + + # do nothing yet + Time.stub :now, Time.gm(2015,3,7,22) do + Token.rotate_database_now(:window => 1.hour) + assert_equal original_name, Token.rotated_database_name + assert_equal 1, count_dbs + end + + # create next db, but don't switch yet. + Time.stub :now, Time.gm(2015,3,7,23) do + Token.rotate_database_now(:window => 1.hour) + assert_equal 2, count_dbs + next_db_name = Token.rotated_database_name(Time.gm(2015,3,8)) + assert original_name != next_db_name + assert database_exists?(next_db_name) + sleep 0.2 # allow time for documents to replicate + assert_equal( + Token.get(doc.id).token, + Token.get(doc.id, database(next_db_name)).token + ) + end + + # use next db + Time.stub :now, Time.gm(2015,3,8) do + Token.rotate_database_now(:window => 1.hour) + assert_equal 2, count_dbs + assert_equal next_db_name, Token.rotated_database_name + token = Token.get(doc.id) + token.update_attributes(:token => 'bbbb') + assert_equal 'bbbb', Token.get(doc.id).token + assert_equal 'aaaa', Token.get(doc.id, database(original_name)).token + end + + # delete prior db + Time.stub :now, Time.gm(2015,3,8,1) do + Token.rotate_database_now(:window => 1.hour) + assert_equal 1, count_dbs + end + end + + private + + def database(db_name) + Token.server.database(Token.db_name_with_prefix(db_name)) + end + + def database_exists?(dbname) + Token.database_exists?(dbname) + end + + def delete_all_dbs(regexp=TEST_DB_RE) + Token.server.databases.each do |db| + if regexp.match(db) + Token.server.database(db).delete! + end + end + end + + def count_dbs(regexp=TEST_DB_RE) + Token.server.databases.grep(regexp).count + end + +end diff --git a/vendor/gems/couchrest_session_store/test/session_document_test.rb b/vendor/gems/couchrest_session_store/test/session_document_test.rb new file mode 100644 index 0000000..2125d10 --- /dev/null +++ b/vendor/gems/couchrest_session_store/test/session_document_test.rb @@ -0,0 +1,27 @@ +require File.expand_path(File.dirname(__FILE__) + '/test_helper') + +class SessionDocumentTest < MiniTest::Test + + def test_storing_session + sid = '1234' + session = {'a' => 'b'} + options = {} + doc = CouchRest::Session::Document.build_or_update(sid, session, options) + doc.save + doc.fetch(sid) + assert_equal session, doc.to_session + end + + def test_storing_session_with_conflict + sid = '1234' + session = {'a' => 'b'} + options = {} + doc = CouchRest::Session::Document.build_or_update(sid, session, options) + doc2 = CouchRest::Session::Document.build_or_update(sid, session, options) + doc.save + doc2.save + doc2.fetch(sid) + assert_equal session, doc2.to_session + end + +end diff --git a/vendor/gems/couchrest_session_store/test/setup_couch.sh b/vendor/gems/couchrest_session_store/test/setup_couch.sh new file mode 100755 index 0000000..731534b --- /dev/null +++ b/vendor/gems/couchrest_session_store/test/setup_couch.sh @@ -0,0 +1,7 @@ +HOST="http://localhost:5984" +echo "couch version :" +curl -X GET $HOST + +curl -X PUT $HOST/couchrest_sessions +curl -X PUT $HOST/couchrest_sessions/_design/Session --data @design/Session.json + diff --git a/vendor/gems/couchrest_session_store/test/stress_test.rb b/vendor/gems/couchrest_session_store/test/stress_test.rb new file mode 100644 index 0000000..b74b132 --- /dev/null +++ b/vendor/gems/couchrest_session_store/test/stress_test.rb @@ -0,0 +1,51 @@ +require_relative 'test_helper' + +# +# This doesn't really test much, but is useful if you want to see what happens +# when you have a lot of documents. +# + +class StressTest < MiniTest::Test + + COUNT = 200 # change to 200,000 if you dare + + class Stress < CouchRest::Model::Base + include CouchRest::Model::Rotation + property :token, String + property :expires_at, Time + rotate_database 'stress_test', :every => 1.day, :expiration_field => :expires_at + end + + def test_stress + delete_all_dbs /^couchrest_stress_test_\d+$/ + + Stress.database! + COUNT.times do |i| + doc = Stress.create!(:token => SecureRandom.hex(32), :expires_at => expires(i)) + end + + Time.stub :now, 1.day.from_now do + Stress.rotate_database_now(:window => 1.hour) + sleep 0.5 + assert_equal (COUNT/100)+1, Stress.database.info["doc_count"] + end + end + + private + + def delete_all_dbs(regexp=TEST_DB_RE) + Stress.server.databases.each do |db| + if regexp.match(db) + Stress.server.database(db).delete! + end + end + end + + def expires(i) + if i % 100 == 0 + 1.hour.from_now.utc + else + 1.hour.ago.utc + end + end +end diff --git a/vendor/gems/couchrest_session_store/test/test_clock.rb b/vendor/gems/couchrest_session_store/test/test_clock.rb new file mode 100644 index 0000000..4170763 --- /dev/null +++ b/vendor/gems/couchrest_session_store/test/test_clock.rb @@ -0,0 +1,12 @@ +class TestClock + attr_accessor :now + + def initialize(tick = 60) + @tick = tick + @now = Time.now + end + + def tick(seconds = nil) + @now += seconds || @tick + end +end diff --git a/vendor/gems/couchrest_session_store/test/test_helper.rb b/vendor/gems/couchrest_session_store/test/test_helper.rb new file mode 100644 index 0000000..32f147d --- /dev/null +++ b/vendor/gems/couchrest_session_store/test/test_helper.rb @@ -0,0 +1,9 @@ +require "rubygems" +gem 'minitest' +require 'minitest/autorun' +require File.expand_path(File.dirname(__FILE__) + '/../lib/couchrest_session_store.rb') +require File.expand_path(File.dirname(__FILE__) + '/couch_tester.rb') +require File.expand_path(File.dirname(__FILE__) + '/test_clock.rb') + +# Create the session db if it does not already exist. +CouchRest::Session::Document.create_database! -- cgit v1.2.3