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

Implement persistent, local reputation + integration with the external Reputation System #214

Merged
merged 30 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4b020c8
initial reputation db
shadeofblue Mar 7, 2024
9154e9a
add reputation `Network` model
shadeofblue Mar 11, 2024
4e435bf
reputation db initial migrations
shadeofblue Mar 11, 2024
a23b943
reputation managent commands
shadeofblue Mar 12, 2024
ead071c
remote reputation system updater
shadeofblue Mar 12, 2024
af66725
populate reputation data from the reputation system
shadeofblue Mar 13, 2024
4437810
fix reputation list display, add progressbar
shadeofblue Mar 13, 2024
1f04742
formatting
shadeofblue Mar 13, 2024
11bde3f
* add local reputation blacklist plugin
shadeofblue Mar 13, 2024
bfc9a5c
* cleanup the hardcoded providers' blacklist
shadeofblue Mar 14, 2024
dbb24e9
lint/format
shadeofblue Mar 14, 2024
1dcadf6
scoring plugin ... in progress
shadeofblue Mar 14, 2024
3f9607b
add reputation-based scoring
shadeofblue Mar 18, 2024
feedd22
move reputation to the main module
shadeofblue Mar 18, 2024
3e319e8
fix
shadeofblue Mar 18, 2024
7249c02
factoryboy + blacklist unit test
shadeofblue Mar 18, 2024
8f1ca08
reputation scorer unit test
shadeofblue Mar 21, 2024
c39b184
make the scorer more elastic
shadeofblue Mar 22, 2024
e7b9efa
add automatic reputation update on webserver start
shadeofblue Mar 22, 2024
bb13f66
Merge branch 'develop' into blue/reputation
shadeofblue Mar 22, 2024
93c17d6
bump the head nodes' score to 100
shadeofblue Mar 25, 2024
fa8a793
remove the hardcoded blacklist altogether
shadeofblue Mar 25, 2024
ac9b968
add updates from the discussion
shadeofblue Mar 25, 2024
2b41892
apply @lucekdudek's suggestions
shadeofblue Mar 25, 2024
5711142
update the name of the reputation scorer's `weights` argument to `par…
shadeofblue Mar 26, 2024
45366b2
Merge branch 'develop' into blue/reputation
shadeofblue Mar 26, 2024
f92db1f
Apply suggestions from code review
shadeofblue Mar 28, 2024
62767e9
apply @approxit's suggestions
shadeofblue Mar 28, 2024
0168113
update with yet one more round of @approxit's comments
shadeofblue Mar 28, 2024
6895c44
format --'
shadeofblue Mar 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@ appdirs = "^1.4.4"
websocat = "^1.12.0"
golem-node = {version = "0.15.0a18", source = "test-pypi"}

setuptools = "*"
tortoise-orm = "^0.20.0"
aerich = "^0.7.2"
prettytable = "^3.10.0"
psutil = "^5.9.8"

setuptools = "*"

[tool.poetry.group.ray.dependencies]
python = "^3.8.1"

Expand All @@ -44,6 +48,7 @@ appdirs = "^1.4.4"

[tool.poetry.scripts]
ray-on-golem = "ray_on_golem.main:main"
rog-db = "ray_on_golem.reputation.admin:admin"

[tool.poetry.group.dev.dependencies]
poethepoet = "^0.22.0"
Expand All @@ -56,6 +61,8 @@ gvmkit-build = "^0.3.13"
dpath = "^2.1.6"
yamlpath = "^3.8.1"
pytest = "^7.4.3"
pytest-asyncio = "^0.23.5.post1"
factory-boy = "^3.3.0"

[[tool.poetry.source]]
name = "test-pypi"
Expand Down
13 changes: 13 additions & 0 deletions ray_on_golem/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from pathlib import Path

import click


def with_datadir(cli_func):
from ray_on_golem.server.settings import DEFAULT_DATADIR

return click.option(
"--datadir",
type=Path,
help=f"Ray on Golem's data directory. By default, uses a system data directory: {DEFAULT_DATADIR}",
)(cli_func)
lucekdudek marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 2 additions & 0 deletions ray_on_golem/main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import click

from ray_on_golem.network_stats import main as network_stats
from ray_on_golem.reputation.cli import reputation_cli
from ray_on_golem.server import main as webserver
from ray_on_golem.server import start, status, stop
from ray_on_golem.version import get_version, version
Expand All @@ -18,6 +19,7 @@ def cli():
cli.add_command(start)
cli.add_command(stop)
cli.add_command(status)
cli.add_command(reputation_cli)


def main():
Expand Down
24 changes: 11 additions & 13 deletions ray_on_golem/network_stats/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
import click
import yaml

from ray_on_golem.cli import with_datadir
from ray_on_golem.network_stats.services import NetworkStatsService
from ray_on_golem.provider.node_provider import GolemNodeProvider
from ray_on_golem.reputation.service import ReputationService
from ray_on_golem.server.services import YagnaService
from ray_on_golem.server.settings import DEFAULT_DATADIR, YAGNA_PATH, get_logging_config
from ray_on_golem.server.settings import YAGNA_PATH, get_logging_config


def validate_config_file(ctx, param, value):
Expand Down Expand Up @@ -54,12 +56,7 @@ def validate_node_type(ctx, param, value):
default=False,
help="Enable verbose logging.",
)
@click.option(
"--datadir",
type=pathlib.Path,
help=f"Ray on Golem's data directory. [default: {DEFAULT_DATADIR}"
" (unless `webserver_datadir` is defined in the cluster config file)]",
)
@with_datadir
def main(*args, **kwargs):
asyncio.run(_network_stats(*args, **kwargs))

Expand Down Expand Up @@ -100,15 +97,16 @@ async def network_stats_service(
datadir=datadir,
)

await yagna_service.init()
await yagna_service.run_payment_fund(network, driver)
async with ReputationService(datadir):
await yagna_service.init()
await yagna_service.run_payment_fund(network, driver)

await service.init(yagna_appkey=yagna_service.yagna_appkey)
await service.init(yagna_appkey=yagna_service.yagna_appkey)

yield service
yield service

await service.shutdown()
await yagna_service.shutdown()
await service.shutdown()
await yagna_service.shutdown()


if __name__ == "__main__":
Expand Down
5 changes: 2 additions & 3 deletions ray_on_golem/network_stats/services/network_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from typing import Dict, Optional, Sequence

from golem.managers import (
BlacklistProviderIdPlugin,
DefaultPaymentManager,
DefaultProposalManager,
NegotiatingPlugin,
Expand All @@ -26,12 +25,12 @@
from golem.resources.proposal.exceptions import ProposalRejected
from ya_market import ApiException

from ray_on_golem.reputation.plugins import ProviderBlacklistPlugin
from ray_on_golem.server.models import NodeConfigData
from ray_on_golem.server.services.golem.golem import DEFAULT_DEMAND_LIFETIME
from ray_on_golem.server.services.golem.helpers.demand_config import DemandConfigHelper
from ray_on_golem.server.services.golem.helpers.manager_stack import ManagerStackNodeConfigHelper
from ray_on_golem.server.services.golem.manager_stack import ManagerStack
from ray_on_golem.server.services.golem.provider_data import PROVIDERS_BLACKLIST

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -243,7 +242,7 @@ async def _create_stack(

plugins = [
self._stats_plugin_factory.create_counter_plugin("Initial"),
BlacklistProviderIdPlugin(PROVIDERS_BLACKLIST.get(payment_network, set())),
ProviderBlacklistPlugin(payment_network),
self._stats_plugin_factory.create_counter_plugin("Not blacklisted"),
]

Expand Down
3 changes: 3 additions & 0 deletions ray_on_golem/reputation/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from ray_on_golem.reputation.service import ReputationService

__all__ = ("ReputationService",)
109 changes: 109 additions & 0 deletions ray_on_golem/reputation/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import asyncio

import click
from aerich import DowngradeError, Migrate

from ray_on_golem.cli import with_datadir
from ray_on_golem.reputation.service import ReputationService


def _format_migrations(migrations):
return [f" {m}\n" for m in migrations]


@click.group(help="Reputation subsystem admin.", context_settings={"show_default": True})
def admin():
...


@admin.command(help="Generate a new migration file.")
@with_datadir
@click.option(
"--name",
type=str,
help="The name of the generated migration file",
)
@click.option(
"--empty",
is_flag=True,
help="If set, just generate an empty migrations file",
)
def migrate(datadir, name, empty):
if empty and not name:
print("Name is required for an empty migration.")
return

async def _migrate():
async with ReputationService(datadir, auto_apply_migrations=False) as service:
await service.migrations.init()
if empty:
migration_name = await Migrate._generate_diff_py(*([name] if name else [])) # noqa
else:
migration_name = await service.migrations.migrate(*([name] if name else []))

if migration_name:
print("Created a migration: ", migration_name)

asyncio.run(_migrate())


@admin.command(help="Show the DB migration files.")
@with_datadir
@click.option(
"-f",
"--show-full",
is_flag=True,
help="If set, just generate an empty migrations file",
)
def updates(datadir, show_full):
async def _updates():
async with ReputationService(datadir, auto_apply_migrations=False) as service:
db_version = await Migrate.get_last_version()
if not db_version:
print("DB uninitialized.")
return

print("Current DB version: ", db_version.version, "\n")

migrations_available = await service.migrations_available()
if migrations_available:
print("Available updates: \n", *_format_migrations(migrations_available))
else:
print("The DB seems to be up to date :)")
if show_full:
print(
"Full migration history: \n",
*_format_migrations(await service.migrations.history()),
)

asyncio.run(_updates())


@admin.command(help="Run all the updates.")
@with_datadir
def upgrade(datadir):
async def _upgrade():
async with ReputationService(datadir, auto_apply_migrations=False) as service:
applied_migrations = await service.apply_migrations()
if applied_migrations:
print("Applied migrations: \n", *_format_migrations(applied_migrations))
else:
print("No new migrations found.")

asyncio.run(_upgrade())


@admin.command(help="Rollback all the migrations up to and including the given version.")
@with_datadir
@click.argument("version", type=int)
def rollback(datadir, version: int):
async def _downgrade():
async with ReputationService(datadir, auto_apply_migrations=False) as service:
try:
rolled_back_migrations = await service.downgrade_migrations(version)
if rolled_back_migrations:
print("Rolled-back migrations: \n", *_format_migrations(rolled_back_migrations))
except DowngradeError:
print("Migration not found in the DB.")

asyncio.run(_downgrade())
Loading