Skip to content

Commit

Permalink
Initial commit with the basic functionalities and updated README
Browse files Browse the repository at this point in the history
  • Loading branch information
jcagarcia committed Oct 23, 2023
1 parent fa48895 commit a814281
Show file tree
Hide file tree
Showing 16 changed files with 1,057 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--color
--format documentation
29 changes: 29 additions & 0 deletions .rspec_status
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
example_id | status | run_time |
--------------------------------------- | ------ | --------------- |
./spec/rollout_spec.rb[1:1:1] | passed | 0.00011 seconds |
./spec/rollout_spec.rb[1:1:2] | passed | 0.00008 seconds |
./spec/rollout_spec.rb[1:1:3:1] | passed | 0.00007 seconds |
./spec/rollout_spec.rb[1:1:3:2] | passed | 0.00007 seconds |
./spec/rollout_spec.rb[1:2:1] | passed | 0.00008 seconds |
./spec/rollout_spec.rb[1:2:2] | passed | 0.00019 seconds |
./spec/rollout_spec.rb[1:3:1:1] | passed | 0.0001 seconds |
./spec/rollout_spec.rb[1:3:2:1] | passed | 0.00091 seconds |
./spec/rollout_spec.rb[1:3:2:2] | passed | 0.00106 seconds |
./spec/rollout_spec.rb[1:3:3:1] | passed | 0.00006 seconds |
./spec/rollout_spec.rb[1:3:4:1:1] | passed | 0.00088 seconds |
./spec/rollout_spec.rb[1:3:5:1:1] | passed | 0.00314 seconds |
./spec/rollout_spec.rb[1:3:5:2:1:1:1:1] | passed | 0.00024 seconds |
./spec/rollout_spec.rb[1:3:5:2:1:1:2:1] | passed | 0.00124 seconds |
./spec/rollout_spec.rb[1:3:5:2:1:1:2:2] | passed | 0.00121 seconds |
./spec/rollout_spec.rb[1:3:5:2:1:2:1] | passed | 0.00028 seconds |
./spec/rollout_spec.rb[1:4:1:1] | passed | 0.00012 seconds |
./spec/rollout_spec.rb[1:4:1:2:1:1] | passed | 0.0002 seconds |
./spec/rollout_spec.rb[1:4:1:2:1:2:1] | passed | 0.00512 seconds |
./spec/rollout_spec.rb[1:4:1:2:1:3:1] | passed | 0.00415 seconds |
./spec/rollout_spec.rb[1:4:1:2:1:4:1] | passed | 0.00598 seconds |
./spec/rollout_spec.rb[1:4:1:2:2:1] | passed | 0.00011 seconds |
./spec/rollout_spec.rb[1:4:1:2:2:2] | passed | 0.0001 seconds |
./spec/rollout_spec.rb[1:4:2:1:1] | passed | 0.00007 seconds |
./spec/rollout_spec.rb[1:4:2:2:1] | passed | 0.0008 seconds |
./spec/rollout_spec.rb[1:4:3:1] | passed | 0.00006 seconds |
./spec/rollout_spec.rb[1:5:1] | passed | 0.00426 seconds |
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Changelog
All changes to `rollout-redis` will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## [0.1.0] - 2023-10-23

- Initial version
13 changes: 13 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM gcr.io/registry-public/ruby:v3-stable

ENV APP /rollout-redis
WORKDIR $APP

RUN apt update && apt install -y build-essential

COPY Gemfile rollout-redis.gemspec Rakefile .rspec $APP/
COPY lib $APP/lib/
COPY spec $APP/spec/

RUN gem install bundler
RUN bundle install -j 10
5 changes: 5 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

source 'https://rubygems.org'

gemspec
41 changes: 41 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
PATH
remote: .
specs:
rollout-redis (0.1.0)
redis (>= 4.0, <= 5)

GEM
remote: https://rubygems.org/
specs:
connection_pool (2.4.1)
diff-lcs (1.5.0)
mock_redis (0.37.0)
redis (5.0.0)
redis-client (~> 0.7)
redis-client (0.17.1)
connection_pool
rspec (3.12.0)
rspec-core (~> 3.12.0)
rspec-expectations (~> 3.12.0)
rspec-mocks (~> 3.12.0)
rspec-core (3.12.2)
rspec-support (~> 3.12.0)
rspec-expectations (3.12.3)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-mocks (3.12.6)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-support (3.12.1)

PLATFORMS
aarch64-linux

DEPENDENCIES
bundler (>= 2.4)
mock_redis (~> 0.37)
rollout-redis!
rspec (~> 3.12)

BUNDLED WITH
2.4.21
56 changes: 56 additions & 0 deletions MIGRATING_FROM_ROLLOUT_GEM.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Migrating from rollout gem 🚀

In this guide you can find the most important changes and differences between the old rollout gem and this gem.

## Deprecations

In order to simplify the features of the first versions of the gem, the following capabilities have been removed:

- Users and groups management have been replaced with a determinator when checking if the feature flag is active or not.
- The ability of changing the information stored inside a feature flag.
- The ability of geting the internal information of a stored feature flag.

This is the complete list of methods that have been removed: `#groups`, `#delete`, `#set`, `#activate_group`, `#deactivate_group`, `#activate_user`, `#deactivate_user`, `#activate_users`, `#deactivate_users`, `#set_users`, `#define_group`, `#user_in_active_users?`, `#inactive?`, `#deactivate_percentage`, `active_in_group?`, `#get`, `#set_feature_data`, `#clear_feature_data`, `#multi_get`, `#features`, `#feature_states`, `#active_features`, `#clear!`, `#exists?`, `#with_feature`.

If you consider some of these methods or features 👆 should be added again to the gem, please, open an issue and we will evaluate it.

## New methods 🎁

New methods has been added: `#with_cache`, `#with_degrade`, `#with_feature_flag`, `#clean_cache`.

## Important changes 🚨

### Keys format and stored data has been changed 🔑

The old [rollout](https://github.com/fetlife/rollout) gem is storing the features flags using `feature:#{name}` as key format. The stored value for each feature flag is a string with this format: `percentage|users|groups|data`. This an example of a current flag stored in redis:

```
Key: "feature:my-feature-flag"
Value: "100|||{}"
```

We have decided to store the data of each feature flag in a more understandable way, so as we don't want to collision with your current stored feature flags this new gem is using `feature-rollout-redis:#{name}` as namespace for storing the new feature flag names in redis.

_NOTE_: This mean that any of your current active feature flags will be taken into consideration!!!

Also, the stored information has been changed and now is a JSON:

```json
{
"percentage": 100,
"requests": 0,
"errors": 0
}
```

#### Migrating feature flags

In order to facilitate the migration, we are offering a method for easily move from the old format to the new one.

```ruby
@rollout.migrate_from_rollout_format
```

This method will NOT remove your old stored feature flags. It will just perform a migration.

Take into consideration that as we are removing users and groups capabilities, you will lost that information after performing the migration. If you want to keep that information, we encourage you to build your own method/script for performing the migration.
14 changes: 14 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.PHONY: build shell irb

build:
-rm -f Gemfile.lock
docker build -t rollout-redis .
@docker create --name tmp_rollout-redis rollout-redis >/dev/null 2>&1
@docker cp tmp_rollout-redis:/rollout-redis/Gemfile.lock . >/dev/null 2>&1
@docker rm tmp_rollout-redis >/dev/null 2>&1

test:
docker run --rm -it -v $(PWD):/rollout-redis rollout-redis bundle exec rspec ${SPEC}

shell:
docker run --rm -it -v $(PWD):/rollout-redis rollout-redis bash
195 changes: 193 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,193 @@
# rollout-redis
Fast feature flags based on the latest Redis versions
# rollout-redis 🚀

[![Gem Version](https://badge.fury.io/rb/rollout-redis.svg)](https://badge.fury.io/rb/rollout-redis)

Fast and easy feature flags based on Redis.

Based on the discontinued [rollout](https://github.com/fetlife/rollout) project, removing some capabilities, including some new features and supporting latest Redis versions.

Topics covered in this README:

- [Install it](#install-it)
- [Quick Start](#quick-start-💨)
- [Advanced features](#advanced-features-🦾)
- [Gradual activation based on percentages](#gradual-activation-based-on-percentages)
- [Caching Feature Flags](#caching-feature-flags)
- [Auto-deactivating flags](#auto-deactivating-flags)
- [Migrating from rollout gem](#migrating-from-rollout-gem-🚨)
- [Changelog](#changelog)
- [Contributing](#contributing)

## Install it

```bash
gem install rollout-redis
```

## Quick Start 💨

Instantiate the `Rollout` class sending a `Redis` instance as a parameter.

```ruby
require 'redis'
require 'rollout'

@redis ||= Redis.new(
host: ENV.fetch('REDIS_HOST'),
port: ENV.fetch('REDIS_PORT')
)
@rollout ||= Rollout.new(@redis)
```

Now you can activate Feature Flags:

```ruby
@rollout.activate('FEATURE_FLAG_NAME') # => true/false
```

Verify if a feature is currently enabled:

```ruby
if @rollout.active?('FEATURE_FLAG_NAME')
# your new code here...
end
```

An alternative to the if check, is to wrap your code under the `with_feature` method. The wrapped code will be performed only if the feature flag is active:

```ruby
@rollout.with_feature('FEATURE_FLAG_NAME') do
# your new code here...
end
```

If there is an issue, you have the option to disable a feature:

```ruby
@rollout.deactivate('FEATURE_FLAG_NAME')
```

## Advanced features 🦾

### Gradual activation based on percentages

When introducing a new feature, it's a recommended practice to gradually enable it for a specific portion of your target audience to evaluate its impact. To achieve this, you can utilize the `activate` method, as shown below:

```ruby
@rollout.activate('FEATURE_FLAG_NAME', 20)
```

Now, to know if a feature flags is enabled, you need to provide a determinator (in this example, we're using the user email):

```ruby
if @rollout.active?('FEATURE_FLAG_NAME', user_email)
# your new code here...
end
```

The gradual activation also works wrapping your code within the `with_feature` method, you just need to provde the determinator you want to use.

```ruby
@rollout.with_feature('FEATURE_FLAG_NAME', user_email) do
# your new code here...
end
```

It's important to note that if you use the `active?` method without specifying a determinator to determine whether this subset of the audience should see the new feature, it will always return `false` since the activation percentage is less than 100%. See:

```ruby
@rollout.activate('FEATURE_FLAG_NAME', 20)
@rollout.active?('FEATURE_FLAG_NAME') # => false
```

### Caching Feature Flags

The Rollout gem is tightly integrated with Redis for feature flag status management. Consequently, occasional connectivity issues between your application and the Redis storage may arise.

To prevent potential application degradation when the Redis storage is unavailable, you can enable feature flag status caching during the gem's instantiation:

```ruby
@rollout ||= Rollout.new(redis).with_cache
```

Additionally, you can specify extra parameters to configure the duration (in seconds) for which the feature flag status is stored in the cache. By default, this duration is set to 300 seconds (5 minutes):

```ruby
@rollout ||= Rollout.new(redis)
.with_cache(expires_in: 300)
```

In the case that you need to clear the cache at any point, you can make use of the `clean_cache` method:

```ruby
@rollout.clean_cache
```

### Auto-deactivating flags

If you want to allow the gem to deactivate your feature flag automatically when a threshold of erros is reached, you can enable the degrade feature using the `with_degrade` method.

```ruby
@rollout ||= Rollout.new(redis)
.with_cache
.with_degrade(sample: 5000, min: 100, threshold: 0.1)
```

So now, instead of using the `active?` method, you need to wrap your new code under the `with_feature` method.

```ruby
@rollout.with_feature('FEATURE_FLAG_NAME') do
# your new feature code here...
end
```

When any unexpected error appears during the wrapped code execution, the Rollout gem will take it into account for automatically deactivating the feature flag if the threshold of errors is reached. All the managed or captured errors inside the wrapped code will not be taken into consideration.

## Migrating from rollout gem 🚨

If you are currently using the unmaintained [rollout](https://github.com/fetlife/rollout) gem, you should consider checking this [migration guide](https://github.com/jcagarcia/rollout-redis/blob/main/MIGRATING_FROM_ROLLOUT_GEM.md) for start using the new `rollout-redis` gem.

## Changelog

If you're interested in seeing the changes and bug fixes between each version of `rollout-redis`, read the [Changelog](https://github.com/jcagarcia/rollout-redis/blob/main/CHANGELOG.md).

## Contributing

We welcome and appreciate contributions from the open-source community. Before you get started, please take a moment to review the guidelines below.

### How to Contribute

1. Fork the repository.
2. Clone the repository to your local machine.
3. Create a new branch for your contribution.
4. Make your changes and ensure they meet project standards.
5. Commit your changes with clear messages.
6. Push your branch to your GitHub repository.
7. Open a pull request in our repository.
8. Participate in code review and address feedback.
9. Once approved, your changes will be merged.

### Development

This project is dockerized. Once you clone the repository, you can use the `Make` commands to build the project.

```shell
make build
```

You can pass the tests running:

```shell
make test
```

### Issue Tracker

Open issues on the GitHub issue tracker with clear information.

### Contributors

* Juan Carlos García - Creator - https://github.com/jcagarcia

The `rollout-redis` gem is based on the discontinued [rollout](https://github.com/fetlife/rollout) project, created by [James Golick](https://github.com/jamesgolick)

7 changes: 7 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

require 'rspec/core/rake_task'

RSpec::Core::RakeTask.new(:spec)

task default: :spec
Loading

0 comments on commit a814281

Please sign in to comment.