Skip to content

Commit

Permalink
Merge branch 'main' into feat(datadog)/custom-source-type
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewelwell committed Feb 14, 2024
2 parents 0ac67c4 + c666d29 commit 1cf58ab
Show file tree
Hide file tree
Showing 145 changed files with 9,555 additions and 2,036 deletions.
26 changes: 2 additions & 24 deletions .github/workflows/api-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,37 +63,15 @@ jobs:

- name: Check for missing migrations
env:
DOTENV_OVERRIDE_FILE: .env-ci-testmon
DOTENV_OVERRIDE_FILE: .env-ci
opts: --no-input --dry-run --check
run: make django-make-migrations

- name: Restore cached testmon data
if:
${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name,
'skip-testmon')}}
id: cache-testmon-restore
uses: actions/cache/restore@v3
with:
enableCrossOsArchive: true
path: |
/home/runner/work/flagsmith/flagsmith/api/.testmondata*
key: testmon-data-python${{ matrix.python-version }}-${{ github.event.pull_request.base.sha }}
restore-keys: testmon-data-python${{ matrix.python-version }}-

- name: Run Tests
env:
DOTENV_OVERRIDE_FILE: .env-ci-testmon
DOTENV_OVERRIDE_FILE: .env-ci
run: make test

- name: Save testmon data cache
id: cache-testmon-save
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
uses: actions/cache/save@v3
with:
path: |
/home/runner/work/flagsmith/flagsmith/api/.testmondata*
key: testmon-data-python${{ matrix.python-version }}-${{github.sha}}

- name: Upload Coverage
uses: codecov/codecov-action@v3
env:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
with:
repository: flagsmith/flagsmith-workflows
token: ${{ secrets.GH_PRIVATE_ACCESS_TOKEN }}
ref: ${{ env.flagsmith_saml_revision }}
ref: ${{ env.FLAGSMITH_WORKFLOWS_REVISION }}
path: ./flagsmith-workflows

- name: Integrate Workflows Logic module
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ checkstyle.txt
.env*
!.env-local
!.env-ci
!.env-ci-testmon
.direnv
.envrc
.tool-versions
Expand Down
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "2.97.1"
".": "2.100.1"
}
54 changes: 54 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,59 @@
# Changelog

## [2.100.1](https://github.com/Flagsmith/flagsmith/compare/v2.100.0...v2.100.1) (2024-02-13)


### Bug Fixes

* **infra:** use correct version number for flagsmith workflows ([#3408](https://github.com/Flagsmith/flagsmith/issues/3408)) ([7adaeb1](https://github.com/Flagsmith/flagsmith/commit/7adaeb123c9ee2e9dfe62d35266db590ec38ab5d))

## [2.100.0](https://github.com/Flagsmith/flagsmith/compare/v2.99.0...v2.100.0) (2024-02-12)


### Features

* Add support for replicas and cross region replicas ([#3300](https://github.com/Flagsmith/flagsmith/issues/3300)) ([bda59f5](https://github.com/Flagsmith/flagsmith/commit/bda59f5982270545c27e600b77e5d06f6229ab93))
* **api-usage:** add environment variable to prevent API usage tracking. ([#3386](https://github.com/Flagsmith/flagsmith/issues/3386)) ([5fa0a1a](https://github.com/Flagsmith/flagsmith/commit/5fa0a1a4694a9c869bdec3604271bcd5ecbc43b4))
* Create split testing for multivariate ([#3235](https://github.com/Flagsmith/flagsmith/issues/3235)) ([ad3ce0e](https://github.com/Flagsmith/flagsmith/commit/ad3ce0e962c85c97fdce1641f3e842ab1c2cfb03))
* try importing rules from LD flags ([#3233](https://github.com/Flagsmith/flagsmith/issues/3233)) ([42634ec](https://github.com/Flagsmith/flagsmith/commit/42634ece4724b869f21e13fc4818e93406c5e0d5))


### Bug Fixes

* Avoid errors when missing subscription information cache id ([#3380](https://github.com/Flagsmith/flagsmith/issues/3380)) ([d9a835f](https://github.com/Flagsmith/flagsmith/commit/d9a835f525ffb3976eabc99dd3210a3804b59744))
* delete project ([#3393](https://github.com/Flagsmith/flagsmith/issues/3393)) ([be544e2](https://github.com/Flagsmith/flagsmith/commit/be544e2351044ebf08eabe67b2ca1315d2fe86ae))
* **redis_cache:** extend DefaultClient class to add support for RedisClusterException ([#3392](https://github.com/Flagsmith/flagsmith/issues/3392)) ([0949963](https://github.com/Flagsmith/flagsmith/commit/0949963a804e4d9aa69120d48181c220f9bdd375))
* **redis-cluster:** add lower socket timeout ([#3401](https://github.com/Flagsmith/flagsmith/issues/3401)) ([37b89b3](https://github.com/Flagsmith/flagsmith/commit/37b89b311d77d570c822ca2800f3719e26826ea1))
* regex tester ([#3395](https://github.com/Flagsmith/flagsmith/issues/3395)) ([64650c6](https://github.com/Flagsmith/flagsmith/commit/64650c630542338c5c41929eeecd40fe443f18de))
* regular expression validation UI ([#3394](https://github.com/Flagsmith/flagsmith/issues/3394)) ([5f13624](https://github.com/Flagsmith/flagsmith/commit/5f13624136c52e74b9dcb9712ad720e8ac11644e))

## [2.99.0](https://github.com/Flagsmith/flagsmith/compare/v2.98.0...v2.99.0) (2024-02-05)


### Features

* Add audit log detail page ([#3356](https://github.com/Flagsmith/flagsmith/issues/3356)) ([e8bc7d3](https://github.com/Flagsmith/flagsmith/commit/e8bc7d3116e9b2ba153044e5539ee5872af13028))


### Bug Fixes

* **revert:** "feat(rate-limit): enable rate limit in production ([#3362](https://github.com/Flagsmith/flagsmith/issues/3362))" ([#3381](https://github.com/Flagsmith/flagsmith/issues/3381)) ([ea3bc3c](https://github.com/Flagsmith/flagsmith/commit/ea3bc3cfd9e451f7ddba0ae493e8531e86b039f6))

## [2.98.0](https://github.com/Flagsmith/flagsmith/compare/v2.97.1...v2.98.0) (2024-02-05)


### Features

* **rate-limit:** enable rate limit in production ([#3362](https://github.com/Flagsmith/flagsmith/issues/3362)) ([f9545f7](https://github.com/Flagsmith/flagsmith/commit/f9545f702079587cde0a6cd24558fee3baf49433))
* **task-processor:** add Task Processor inputs as env vars ([#3355](https://github.com/Flagsmith/flagsmith/issues/3355)) ([789898c](https://github.com/Flagsmith/flagsmith/commit/789898c47a3fa726bbd1c99d7d7700ae1eb4f3ef))


### Bug Fixes

* **audit:** add details for override creation ([#3359](https://github.com/Flagsmith/flagsmith/issues/3359)) ([a888f29](https://github.com/Flagsmith/flagsmith/commit/a888f291eafd3b113233cf30b19594a37f8fb13a))
* Long `DELETE` project call ([#3360](https://github.com/Flagsmith/flagsmith/issues/3360)) ([aca0fc5](https://github.com/Flagsmith/flagsmith/commit/aca0fc54f5902d4c9ba5630aa8af35b66b7c0799))
* **webhooks:** prevent unnecessary organisation webhook tasks ([#3365](https://github.com/Flagsmith/flagsmith/issues/3365)) ([ec32ce7](https://github.com/Flagsmith/flagsmith/commit/ec32ce7dfc86ccb350f2a9e1af8c3f85f6c154b7))

## [2.97.1](https://github.com/Flagsmith/flagsmith/compare/v2.97.0...v2.97.1) (2024-02-02)


Expand Down
3 changes: 0 additions & 3 deletions api/.env-ci-testmon

This file was deleted.

3 changes: 0 additions & 3 deletions api/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,3 @@ features/workflows/logic/

# Unit test coverage
.coverage

# pytest-testmon files
.testmondata*
37 changes: 34 additions & 3 deletions api/api/urls/v1.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from app_analytics.views import SDKAnalyticsFlags, SelfHostedTelemetryAPIView
from django.conf import settings
from django.conf.urls import url
from django.urls import include
from django.urls import include, path
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
from rest_framework import authentication, permissions, routers
Expand Down Expand Up @@ -47,8 +48,12 @@
url(r"^flags/$", SDKFeatureStates.as_view(), name="flags"),
url(r"^identities/$", SDKIdentities.as_view(), name="sdk-identities"),
url(r"^traits/", include(traits_router.urls), name="traits"),
url(r"^analytics/flags/$", SDKAnalyticsFlags.as_view()),
url(r"^analytics/telemetry/$", SelfHostedTelemetryAPIView.as_view()),
url(r"^analytics/flags/$", SDKAnalyticsFlags.as_view(), name="analytics-flags"),
url(
r"^analytics/telemetry/$",
SelfHostedTelemetryAPIView.as_view(),
name="analytics-telemetry",
),
url(
r"^environment-document/$",
SDKEnvironmentAPIView.as_view(),
Expand All @@ -67,3 +72,29 @@
name="schema-swagger-ui",
),
]

if settings.SPLIT_TESTING_INSTALLED:
from split_testing.views import (
ConversionEventTypeView,
CreateConversionEventView,
SplitTestViewSet,
)

split_testing_router = routers.DefaultRouter()
split_testing_router.register(r"", SplitTestViewSet, basename="split-tests")

urlpatterns += [
url(
r"^split-testing/", include(split_testing_router.urls), name="split-testing"
),
url(
r"^split-testing/conversion-events/",
CreateConversionEventView.as_view(),
name="conversion-events",
),
path(
"conversion_event_types/",
ConversionEventTypeView.as_view(),
name="conversion-event-types",
),
]
8 changes: 8 additions & 0 deletions api/api/urls/v2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from app_analytics.views import SDKAnalyticsFlagsV2
from django.conf.urls import url

app_name = "v2"

urlpatterns = [
url(r"^analytics/flags/$", SDKAnalyticsFlagsV2.as_view(), name="analytics-flags")
]
2 changes: 2 additions & 0 deletions api/app/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class ImproperlyConfiguredError(RuntimeError):
pass
93 changes: 91 additions & 2 deletions api/app/routers.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,79 @@
import logging
import random
from enum import Enum

from django.conf import settings
from django.core.cache import cache
from django.db import connections

from .exceptions import ImproperlyConfiguredError

logger = logging.getLogger(__name__)

CONNECTION_CHECK_CACHE_TTL = 2


class ReplicaReadStrategy(Enum):
DISTRIBUTED = "DISTRIBUTED"
SEQUENTIAL = "SEQUENTIAL"


def connection_check(database: str) -> bool:
try:
conn = connections.create_connection(database)
conn.connect()
usable = conn.is_usable()
if not usable:
logger.warning(
f"Unable to access database {database} during connection check"
)
except Exception:
usable = False
logger.error(
"Encountered exception during connection",
exc_info=True,
)

if usable:
cache.set(
f"db_connection_active.{database}", "online", CONNECTION_CHECK_CACHE_TTL
)
else:
cache.set(
f"db_connection_active.{database}", "offline", CONNECTION_CHECK_CACHE_TTL
)

return usable


class PrimaryReplicaRouter:
def db_for_read(self, model, **hints):
if settings.NUM_DB_REPLICAS == 0:
return "default"
return random.choice(
[f"replica_{i}" for i in range(1, settings.NUM_DB_REPLICAS + 1)]

replicas = [f"replica_{i}" for i in range(1, settings.NUM_DB_REPLICAS + 1)]
replica = self._get_replica(replicas)
if replica:
# This return is the most likely as replicas should be
# online and properly functioning.
return replica

# Since no replicas are available, fall back to the cross
# region replicas which have worse availability.
cross_region_replicas = [
f"cross_region_replica_{i}"
for i in range(1, settings.NUM_CROSS_REGION_DB_REPLICAS + 1)
]

cross_region_replica = self._get_replica(cross_region_replicas)
if cross_region_replica:
return cross_region_replica

# No available replicas, so fallback to the default.
logger.warning(
"Unable to serve any available replicas, falling back to default database"
)
return "default"

def db_for_write(self, model, **hints):
return "default"
Expand All @@ -22,6 +86,10 @@ def allow_relation(self, obj1, obj2, **hints):
db_set = {
"default",
*[f"replica_{i}" for i in range(1, settings.NUM_DB_REPLICAS + 1)],
*[
f"cross_region_replica_{i}"
for i in range(1, settings.NUM_CROSS_REGION_DB_REPLICAS + 1)
],
}
if obj1._state.db in db_set and obj2._state.db in db_set:
return True
Expand All @@ -30,6 +98,27 @@ def allow_relation(self, obj1, obj2, **hints):
def allow_migrate(self, db, app_label, model_name=None, **hints):
return db == "default"

def _get_replica(self, replicas: list[str]) -> None | str:
while replicas:
if settings.REPLICA_READ_STRATEGY == ReplicaReadStrategy.DISTRIBUTED.value:
database = random.choice(replicas)
elif settings.REPLICA_READ_STRATEGY == ReplicaReadStrategy.SEQUENTIAL.value:
database = replicas[0]
else:
raise ImproperlyConfiguredError(
f"Unknown REPLICA_READ_STRATEGY {settings.REPLICA_READ_STRATEGY}"
)

replicas.remove(database)
db_cache = cache.get(f"db_connection_active.{database}")
if db_cache == "online":
return database
if db_cache == "offline":
continue

if connection_check(database):
return database


class AnalyticsRouter:
route_app_labels = ["app_analytics"]
Expand Down
Loading

0 comments on commit 1cf58ab

Please sign in to comment.