Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MnesiaAdapter module for simplified Mnesia database operations #99

Merged
merged 10 commits into from
Jul 4, 2024
Merged
59 changes: 59 additions & 0 deletions .github/workflows/semantic-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Semantic-Release

on:
push:
branches: [ main ]

permissions:
contents: write
issues: write
pull-requests: write
id-token: write

jobs:
release:
environment: SEMANTIC_RELEASE_ENV
runs-on: ubuntu-latest

env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
HEX_API_KEY: ${{ secrets.HEX_API_KEY }}
GITHUB_API_URL: ${{ vars.GH_API_URL }}
PUBLISH_ARTIFACT: ${{ vars.PUBLISH_ARTIFACT }}
ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }}
OTP_VERSION: ${{ vars.OTP_VERSION }}
SKIP_GIT_HOOKS: 'true'

steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x

- name: Set up Elixir ${{ env.ELIXIR_VERSION }} and Otp ${{ env.OTP_VERSION }}
uses: erlef/setup-beam@v1
with:
elixir-version: ${{ env.ELIXIR_VERSION }}
otp-version: ${{ env.OTP_VERSION }}

- name: Setup semantic-release
run: npm install -g semantic-release @semantic-release/changelog @semantic-release/github -D

- name: Call semantic-release
run: npx -p @semantic-release/changelog semantic-release

- name: Set variables From git tag command
if: ${{ ( env.PUBLISH_ARTIFACT == 'true' ) }}
run: |
echo "$VER"
echo "RELEASE_VERSION=$(git tag | sort --version-sort | tail -n1 | tr -d 'v')" >> $GITHUB_ENV

- name: Install Mix dependencies
if: ${{ ( env.PUBLISH_ARTIFACT == 'true' ) }}
run: mix do local.hex --force, local.rebar --force && mix do deps.clean --unused, deps.get, deps.compile && mix deps.compile

- name: Publish to HEX
if: ${{ ( env.PUBLISH_ARTIFACT == 'true' ) }}
run: mix hex.publish --replace --yes
9 changes: 9 additions & 0 deletions .releaserc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"repositoryUrl": "https://github.com/bancolombia/distributed-performance-analyzer.git",
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/github"
]
}
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
[![Forks][forks-shield]][forks-url]
[![Stars][stars-shield]][stars-url]
[![Issues][issues-shield]][issues-url]
[![semantic-release: angular][semantic-release-url-badge]][semantic-release-url]


[![Quality Gate Status][sonarcloud-quality-gate-shield]][sonarcloud-url]
[![Maintainability Rating][sonarcloud-maintainability-shield]][sonarcloud-url]
Expand Down Expand Up @@ -165,6 +167,8 @@ vs Mean Latency.
![Example 2 - Latency](assets/dresults_example2.png)


[semantic-release-url-badge]: https://img.shields.io/badge/semantic--release-angular-e10079?logo=semantic-release
[semantic-release-url]: https://github.com/semantic-release/semantic-release
[scorecards-shield]: https://github.com/bancolombia/distributed-performance-analyzer/actions/workflows/scorecards-analysis.yml/badge.svg
[scorecards-url]: https://github.com/bancolombia/distributed-performance-analyzer/actions/workflows/scorecards-analysis.yml
[docker-shield]: https://img.shields.io/docker/pulls/bancolombia/distributed-performance-analyzer
Expand Down
32 changes: 32 additions & 0 deletions SEMANTIC-RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Semantic Release
## Commit message format
semantic-release utiliza los mensajes de confirmación para determinar el impacto de los cambios en el release; Siguiendo las convenciones para mensajes de confirmación, semantic-release determina automáticamente el siguiente número de versión semántica, genera un registro de cambios y publica la versión.

De forma predeterminada, la liberación semántica utiliza
convenciones de mensajes de confirmación angular. [convenciones de mensajes de confirmación angular.](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format)

Se pueden utilizar herramientas como commitizen o commit-lint para ayudar a los contribuyentes y hacer cumplir los mensajes de confirmación válidos.

La siguiente tabla muestra qué mensaje de confirmación le proporciona qué tipo de versión cuando se ejecuta la versión semántica (usando la configuración predeterminada):

| Commit Message | Release type |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| `fix(pencil): stop graphite breaking when too much pressure applied` | ~~Patch~~ Fix Release |
| `feat(pencil): add 'graphiteWidth' option` | ~~Minor~~ Feature Release |
| `perf(pencil): remove graphiteWidth option`<br><br>`BREAKING CHANGE: The graphiteWidth option has been removed.`<br>`The default graphite width of 10mm is always used for performance reasons.` | ~~Major~~ Breaking Release <br /> (Note that the `BREAKING CHANGE: ` token must be in the footer of the commit) |
**[link a la fuente.](https://github.com/semantic-release/semantic-release?tab=readme-ov-file#Commit%20message%20format)**

dentro de la documentación de angular sobre convenciones de mensajes se tienen estos adicionales:

- **build:** cambios que afectan el sistema de compilación o las dependencias externas (alcances de ejemplo: gulp, broccoli, npm).
- **ci:** Cambios en nuestros archivos y scripts de configuración de CI (ejemplos: CircleCi, SauceLabs).
- **docs:** La documentación solo cambia.
- **feat:** una nueva característica.
- **fix:** una corrección de errores.
- **perf:** Un cambio de código que mejora el rendimiento.
- **refactor:** un cambio de código que no corrige un error ni agrega una característica.
- **test:** agregar pruebas faltantes o corregir pruebas existentes.

## excluir commit del análisis del plugin:

Todas las confirmaciones que contengan [skip release] or [release skip] en su mensaje se excluirán del análisis de confirmación y no participarán en la determinación del tipo de versión.
2 changes: 1 addition & 1 deletion lib/domain/use_cases/metrics_collector_use_case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ defmodule DistributedPerformanceAnalyzer.Domain.UseCase.MetricsCollectorUseCase
end

def get_metrics do
GenServer.call({:global, __MODULE__}, :get_metrics)
GenServer.call({:global, __MODULE__}, :get_metrics, 30_000)
end

def clean_metrics do
Expand Down
137 changes: 137 additions & 0 deletions lib/infrastructure/driven_adapters/mnesia/mnesia.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
defmodule MnesiaAdapter do
@moduledoc """
Provides a high-level interface for interacting with the Mnesia database.

This module encapsulates common Mnesia operations such as starting the database,
creating tables, writing records, and reading data. It also includes logging
for better observability and wraps asynchronous operations in Tasks.
"""

require Logger
alias :mnesia, as: Mnesia

@type table_name :: atom()
@type table_attributes :: [{atom(), atom()}]
@type record :: tuple()
@type result :: :ok | {:error, term()}

@doc """
Starts the Mnesia database.

This function attempts to start Mnesia and logs the result. If Mnesia
is already running, it returns :ok.

Returns:
* `:ok` if Mnesia started successfully or was already running
* `{:error, reason}` if there was an error starting Mnesia
"""
@spec start() :: result()
def start do
case Mnesia.start() do
:ok ->
Logger.info("#{__MODULE__}: Mnesia started successfully")
:ok

{:error, {:already_started, _node}} ->
Logger.info("#{__MODULE__}: Mnesia already started")
:ok

{:error, reason} ->
Logger.error("#{__MODULE__}: Error starting Mnesia: #{inspect(reason)}")
{:error, reason}
end
end

@doc """
Creates a new Mnesia table.

This function creates a new table in Mnesia with the given name and attributes.
It logs the result of the operation.

Parameters:
* `table`: The name of the table to create (atom)
* `attributes`: A list of attribute definitions for the table

Returns:
* `:ok` if the table was created successfully
* `{:error, reason}` if there was an error creating the table
"""
@spec create_table(table_name(), table_attributes()) :: result()
def create_table(table, attributes) when is_atom(table) and is_list(attributes) do
Logger.info("#{__MODULE__}: Creating Mnesia table: #{table}")

case Mnesia.create_table(table, attributes: attributes) do
{:atomic, :ok} ->
Logger.info("#{__MODULE__}: Mnesia table #{table} created successfully")
:ok

{:aborted, reason} ->
Logger.error("#{__MODULE__}: Error creating Mnesia table #{table}: #{inspect(reason)}")
{:error, reason}
end
end

@doc """
Writes a record to Mnesia asynchronously.

This function writes the given record to Mnesia using a dirty write operation.
The operation is wrapped in a Task for asynchronous execution.

Parameters:
* `record`: The record to write to Mnesia (tuple)

Returns:
* A `Task` that will resolve to `:ok` if the write was successful,
or `{:error, reason}` if there was an error
"""
@spec write(record()) :: Task.t()
def write(record) do
Task.async(fn ->
Logger.info("#{__MODULE__}: Writing data to Mnesia: #{inspect(record)}")

case Mnesia.dirty_write(record) do
:ok ->
Logger.info("#{__MODULE__}: Data written to Mnesia successfully")
:ok

{:error, reason} ->
Logger.error("#{__MODULE__}: Error writing data to Mnesia: #{inspect(reason)}")
{:error, reason}
end
end)
end

@doc """
Reads a record from Mnesia asynchronously.

This function reads a record from Mnesia using a dirty read operation.
The operation is wrapped in a Task for asynchronous execution.

Parameters:
* `key`: The key to read from Mnesia (can be a tuple for composite keys)

Returns:
* A `Task` that will resolve to `{:ok, [record]}` if the read was successful,
`{:ok, []}` if no record was found, or `{:error, reason}` if there was an error
"""
@spec read(record()) :: Task.t()
def read(key) do
Task.async(fn ->
Logger.info("#{__MODULE__}: Reading data from Mnesia: #{inspect(key)}")

case Mnesia.dirty_read(key) do
[] ->
Logger.info("#{__MODULE__}: No data found in Mnesia")
{:ok, []}

result when is_list(result) ->
Logger.info("#{__MODULE__}: Data read from Mnesia successfully: #{inspect(result)}")
{:ok, result}

{:error, reason} ->
Logger.error("#{__MODULE__}: Error reading data from Mnesia: #{inspect(reason)}")
{:error, reason}
end
end)
end
end
16 changes: 15 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ defmodule DistributedPerformanceAnalyzer.MixProject do
],
deps: deps(),
aliases: aliases(),
metrics: true
metrics: true,
package: package()
]
end

Expand Down Expand Up @@ -88,4 +89,17 @@ defmodule DistributedPerformanceAnalyzer.MixProject do
{:poolboy, "~> 1.5"}
]
end

defp package() do
[
# This option is only needed when you don't want to use the OTP application name
name: "distributed_performance_analyzer",
# The organization the package belongs to. The package will be published to the organization repository, defaults = i"hexpm" repository.
organization: "bancolombia",
files: ["assets", "config", "hooks", "lib", "rel", "test", "Dockerfile", "LICENSE", "SECURITY.md", "README.md", "coveralls.json", "mix.lock", "mix.exs", "sonar-project.properties", ".formatter.exs", ".credo.exs", ".gitignore", ".dockerignore"],
maintainers: ["Brayan Batista Zúniga", "Alejandro Jose Tortolero Machado", "Juan David Giraldo Marin"],
licenses: ["MIT License"],
links: %{"GitHub" => "https://github.com/bancolombia/distributed-performance-analyzer.git"}
]
end
end
Loading