diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 0000000000000000000000000000000000000000..1e43ca64c68a9e4948ee5611c934d34298a3416e --- /dev/null +++ b/alembic.ini @@ -0,0 +1,85 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = alembic + +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# timezone to use when rendering the date +# within the migration file as well as the filename. +# string value is passed to dateutil.tz.gettz() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the +# "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; this defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path +# version_locations = %(here)s/bar %(here)s/bat alembic/versions + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = postgresql://hive:aMom3ieBieh9Ish0huiY2eGhreiThu7j@localhost/hive + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks=black +# black.type=console_scripts +# black.entrypoint=black +# black.options=-l 79 + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/alembic/README b/alembic/README new file mode 100644 index 0000000000000000000000000000000000000000..98e4f9c44effe479ed38c66ba922e7bcc672916f --- /dev/null +++ b/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/alembic/env.py b/alembic/env.py new file mode 100644 index 0000000000000000000000000000000000000000..70518a2eef734a8fffcd787cfa397309469f8e76 --- /dev/null +++ b/alembic/env.py @@ -0,0 +1,77 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = None + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/alembic/script.py.mako b/alembic/script.py.mako new file mode 100644 index 0000000000000000000000000000000000000000..2c0156303a8df3ffdc9de87765bf801bf6bea4a5 --- /dev/null +++ b/alembic/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/alembic/versions/bf9df12d4f01_jsalyers_update_some_indexes.py b/alembic/versions/bf9df12d4f01_jsalyers_update_some_indexes.py new file mode 100644 index 0000000000000000000000000000000000000000..d9623345bbbbc9a33fb20f0ecacb4dda532f3607 --- /dev/null +++ b/alembic/versions/bf9df12d4f01_jsalyers_update_some_indexes.py @@ -0,0 +1,26 @@ +"""jsalyers-update-some-indexes + +Revision ID: bf9df12d4f01 +Revises: +Create Date: 2020-05-04 15:54:28.863707 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'bf9df12d4f01' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_index('hive_posts_cache_author_permlink_idx', 'hive_posts_cache', ['author', 'permlink']) + op.create_index('hive_posts_cache_post_id_author_permlink_idx', 'hive_posts_cache', ['post_id', 'author', 'permlink']) + + +def downgrade(): + op.drop_index('hive_posts_cache_author_permlink_idx') + op.drop_index('hive_posts_cache_post_id_author_permlink_idx') diff --git a/build.sh b/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..1b2bfebd9bef7206cd286d21699f7eec72b7c42b --- /dev/null +++ b/build.sh @@ -0,0 +1,9 @@ +echo Build started on `date` +export IMAGE_TAG=`git ls-remote --heads origin | grep $(git rev-parse HEAD) | cut -d / -f 3` +if [ "$IMAGE_TAG" = "master" ] ; then export IMAGE_TAG=latest ; fi +export REPO_PATH=`git rev-parse --show-toplevel` +export REPO_NAME=`basename $REPO_PATH` +export IMAGE_REPO_NAME="hive/$REPO_NAME" +export SOURCE_COMMIT=`git rev-parse HEAD` +echo Building branch $IMAGE_TAG from $IMAGE_REPO_NAME +docker build . -t $IMAGE_REPO_NAME:$IMAGE_TAG --build-arg SOURCE_COMMIT="${SOURCE_COMMIT}" --build-arg DOCKER_TAG="${IMAGE_TAG}" diff --git a/hive/server/condenser_api/cursor.py b/hive/server/condenser_api/cursor.py index 2e3b42ba437f74b92b74d46987daad292bb51928..1b3fa5caa7c3aab810a0b715c30372fe19481e0f 100644 --- a/hive/server/condenser_api/cursor.py +++ b/hive/server/condenser_api/cursor.py @@ -166,28 +166,30 @@ async def pids_by_query(db, sort, start_author, start_permlink, limit, tag): where.append("post_id IN (%s)" % sql) start_id = None - if start_permlink: - start_id = await _get_post_id(db, start_author, start_permlink) - if not start_id: - return [] - - sql = "%s <= (SELECT %s FROM %s WHERE post_id = :start_id)" + if start_permlink and start_author: + sql = "%s <= (SELECT %s FROM %s WHERE post_id = (SELECT post_id FROM hive_posts_cache WHERE author = :start_author AND permlink= :start_permlink))" where.append(sql % (field, field, table)) - sql = ("SELECT post_id FROM %s WHERE %s ORDER BY %s DESC LIMIT :limit" - % (table, ' AND '.join(where), field)) + columns = ['hive_posts_cache.post_id', 'hive_posts_cache.author', 'hive_posts_cache.permlink', + 'hive_posts_cache.title', 'hive_posts_cache.body', 'hive_posts_cache.category', + 'hive_posts_cache.depth', 'hive_posts_cache.promoted', + 'hive_posts_cache.payout', 'hive_posts_cache.payout_at', 'hive_posts_cache.is_paidout', + 'hive_posts_cache.children', 'hive_posts_cache.votes', 'hive_posts_cache.created_at', + 'hive_posts_cache.updated_at', 'hive_posts_cache.rshares', 'hive_posts_cache.raw_json', + 'hive_posts_cache.json'] + sql = ("SELECT %s FROM %s WHERE %s ORDER BY %s DESC LIMIT :limit" + % (', '.join(columns), table, ' AND '.join(where), field)) - return await db.query_col(sql, tag=tag, start_id=start_id, limit=limit) + #return await db.query_col(sql, tag=tag, start_id=start_id, limit=limit) + return [sql, tag, start_id, limit] async def pids_by_blog(db, account: str, start_author: str = '', start_permlink: str = '', limit: int = 20): """Get a list of post_ids for an author's blog.""" - account_id = await _get_account_id(db, account) - seek = '' start_id = None - if start_permlink: + if start_permlink and start_author: start_id = await _get_post_id(db, start_author, start_permlink) if not start_id: return [] diff --git a/hive/server/condenser_api/methods.py b/hive/server/condenser_api/methods.py index fe0a5bb9cf730659f0430aa8c888eca5e98795f2..fd76750eedb690028821863169506b8d21b7b548 100644 --- a/hive/server/condenser_api/methods.py +++ b/hive/server/condenser_api/methods.py @@ -4,6 +4,7 @@ from functools import wraps import hive.server.condenser_api.cursor as cursor from hive.server.condenser_api.objects import load_posts, load_posts_reblogs, resultset_to_posts +from hive.server.condenser_api.objects import _mute_votes, _condenser_post_object from hive.server.common.helpers import ( ApiError, return_error_info, @@ -13,6 +14,7 @@ from hive.server.common.helpers import ( valid_offset, valid_limit, valid_follow_type) +from hive.server.common.mutes import Mutes # pylint: disable=too-many-arguments,line-too-long,too-many-lines @@ -101,12 +103,34 @@ async def get_content(context, author: str, permlink: str): db = context['db'] valid_account(author) valid_permlink(permlink) - post_id = await cursor.get_post_id(db, author, permlink) - if not post_id: - return {'id': 0, 'author': '', 'permlink': ''} - posts = await load_posts(db, [post_id]) - assert posts, 'post was not found in cache' - return posts[0] + + sql = """ + ---get_content + SELECT hive_posts_cache.post_id, hive_posts_cache.author, hive_posts_cache.permlink, + hive_posts_cache.title, hive_posts_cache.body, hive_posts_cache.category, hive_posts_cache.depth, + hive_posts_cache.promoted, hive_posts_cache.payout, hive_posts_cache.payout_at, + hive_posts_cache.is_paidout, hive_posts_cache.children, hive_posts_cache.votes, + hive_posts_cache.created_at, hive_posts_cache.updated_at, hive_posts_cache.rshares, + hive_posts_cache.raw_json, hive_posts_cache.json, hive_accounts.reputation AS author_rep + FROM hive_posts_cache JOIN hive_accounts ON (hive_posts_cache.author = hive_accounts.name) + JOIN hive_posts ON (hive_posts_cache.post_id = hive_posts.id) + WHERE hive_posts_cache.author = :author AND hive_posts_cache.permlink = :permlink AND NOT hive_posts.is_deleted + """ + + result = await db.query_all(sql, author=author, permlink=permlink) + result = dict(result[0]) + post = _condenser_post_object(result, 0) + post['active_votes'] = _mute_votes(post['active_votes'], Mutes.all()) + + assert post, 'post was not found in cache' + return post + + #post_id = await cursor.get_post_id(db, author, permlink) + #if not post_id: + # return {'id': 0, 'author': '', 'permlink': ''} + #posts = await load_posts(db, [post_id]) + #assert posts, 'post was not found in cache' + #return posts[0] @return_error_info @@ -116,7 +140,9 @@ async def get_content_replies(context, author: str, permlink: str): valid_account(author) valid_permlink(permlink) - sql = """SELECT post_id, author, permlink, title, body, category, depth, + sql = """ + --get_content_replies + SELECT post_id, author, permlink, title, body, category, depth, promoted, payout, payout_at, is_paidout, children, votes, created_at, updated_at, rshares, raw_json, json FROM hive_posts_cache WHERE post_id IN ( @@ -156,6 +182,37 @@ def nested_query_compat(function): return function(*args, **kwargs) return wrapper +@return_error_info +@nested_query_compat +async def get_discussions_by(discussion_type, context, start_author: str = '', + start_permlink: str = '', limit: int = 20, + tag: str = None, truncate_body: int = 0, + filter_tags: list = None): + """ Common implementation for get_discussions_by calls """ + assert not filter_tags, 'filter tags not supported' + assert discussion_type in ['trending', 'hot', 'created', 'promoted', + 'payout', 'payout_comments'], 'invalid discussion type' + + db = context['db'] + query_information = await cursor.pids_by_query( + db, + discussion_type, + valid_account(start_author, allow_empty=True), + valid_permlink(start_permlink, allow_empty=True), + valid_limit(limit, 100), + valid_tag(tag, allow_empty=True)) + + assert len(query_information) == 4, 'generated query is malformed, aborting' + sql = query_information[0] + sql = "---get_discussions_by_" + discussion_type + "\r\n" + sql + sql_tag = query_information[1] + sql_start_id = query_information[2] + sql_limit = query_information[3] + + result = await db.query_all(sql, tag=sql_tag, start_id=sql_start_id, limit=sql_limit, start_author=start_author, start_permlink=start_permlink) + posts = await resultset_to_posts(db=db, resultset=result, truncate_body=0) + return posts + @return_error_info @nested_query_compat @@ -164,14 +221,9 @@ async def get_discussions_by_trending(context, start_author: str = '', start_per truncate_body: int = 0, filter_tags: list = None): """Query posts, sorted by trending score.""" assert not filter_tags, 'filter_tags not supported' - ids = await cursor.pids_by_query( - context['db'], - 'trending', - valid_account(start_author, allow_empty=True), - valid_permlink(start_permlink, allow_empty=True), - valid_limit(limit, 100), - valid_tag(tag, allow_empty=True)) - return await load_posts(context['db'], ids, truncate_body=truncate_body) + results = await get_discussions_by('trending', context, start_author, start_permlink, + limit, tag, truncate_body, filter_tags) + return results @return_error_info @@ -181,14 +233,9 @@ async def get_discussions_by_hot(context, start_author: str = '', start_permlink truncate_body: int = 0, filter_tags: list = None): """Query posts, sorted by hot score.""" assert not filter_tags, 'filter_tags not supported' - ids = await cursor.pids_by_query( - context['db'], - 'hot', - valid_account(start_author, allow_empty=True), - valid_permlink(start_permlink, allow_empty=True), - valid_limit(limit, 100), - valid_tag(tag, allow_empty=True)) - return await load_posts(context['db'], ids, truncate_body=truncate_body) + results = await get_discussions_by('hot', context, start_author, start_permlink, + limit, tag, truncate_body, filter_tags) + return results @return_error_info @@ -198,14 +245,9 @@ async def get_discussions_by_promoted(context, start_author: str = '', start_per truncate_body: int = 0, filter_tags: list = None): """Query posts, sorted by promoted amount.""" assert not filter_tags, 'filter_tags not supported' - ids = await cursor.pids_by_query( - context['db'], - 'promoted', - valid_account(start_author, allow_empty=True), - valid_permlink(start_permlink, allow_empty=True), - valid_limit(limit, 100), - valid_tag(tag, allow_empty=True)) - return await load_posts(context['db'], ids, truncate_body=truncate_body) + results = await get_discussions_by('promoted', context, start_author, start_permlink, + limit, tag, truncate_body, filter_tags) + return results @return_error_info @@ -215,14 +257,9 @@ async def get_discussions_by_created(context, start_author: str = '', start_perm truncate_body: int = 0, filter_tags: list = None): """Query posts, sorted by creation date.""" assert not filter_tags, 'filter_tags not supported' - ids = await cursor.pids_by_query( - context['db'], - 'created', - valid_account(start_author, allow_empty=True), - valid_permlink(start_permlink, allow_empty=True), - valid_limit(limit, 100), - valid_tag(tag, allow_empty=True)) - return await load_posts(context['db'], ids, truncate_body=truncate_body) + results = await get_discussions_by('created', context, start_author, start_permlink, + limit, tag, truncate_body, filter_tags) + return results @return_error_info @@ -233,14 +270,46 @@ async def get_discussions_by_blog(context, tag: str = None, start_author: str = """Retrieve account's blog posts, including reblogs.""" assert tag, '`tag` cannot be blank' assert not filter_tags, 'filter_tags not supported' - ids = await cursor.pids_by_blog( - context['db'], - valid_account(tag), - valid_account(start_author, allow_empty=True), - valid_permlink(start_permlink, allow_empty=True), - valid_limit(limit, 100)) - return await load_posts(context['db'], ids, truncate_body=truncate_body) + valid_account(tag) + valid_account(start_author, allow_empty=True) + valid_permlink(start_permlink, allow_empty=True) + valid_limit(limit, 100) + + sql = """ + ---get_discussions_by_blog + SELECT hive_posts_cache.post_id, hive_posts_cache.author, hive_posts_cache.permlink, + hive_posts_cache.title, hive_posts_cache.body, hive_posts_cache.category, hive_posts_cache.depth, + hive_posts_cache.promoted, hive_posts_cache.payout, hive_posts_cache.payout_at, + hive_posts_cache.is_paidout, hive_posts_cache.children, hive_posts_cache.votes, + hive_posts_cache.created_at, hive_posts_cache.updated_at, hive_posts_cache.rshares, + hive_posts_cache.raw_json, hive_posts_cache.json, hive_accounts.reputation AS author_rep + FROM hive_posts_cache JOIN hive_accounts ON (hive_posts_cache.author = hive_accounts.name) + JOIN hive_posts ON (hive_posts_cache.post_id = hive_posts.id) + WHERE NOT hive_posts.is_deleted AND hive_posts_cache.post_id IN + (SELECT post_id FROM hive_feed_cache JOIN hive_accounts ON (hive_feed_cache.account_id = hive_accounts.id) WHERE hive_accounts.name = :author) + """ + if start_author and start_permlink != '': + sql += """ + AND hive_posts_cache.created_at <= (SELECT created_at from hive_posts_cache where author = :start_author AND permlink = :start_permlink) + """ + + sql += """ + ORDER BY hive_posts_cache.created_at DESC + LIMIT :limit + """ + db = context['db'] + result = await db.query_all(sql, author=tag, start_author=start_author, start_permlink=start_permlink, limit=limit) + posts_by_id = [] + + for row in result: + row = dict(row) + post = _condenser_post_object(row, truncate_body=truncate_body) + post['active_votes'] = _mute_votes(post['active_votes'], Mutes.all()) + #posts_by_id[row['post_id']] = post + posts_by_id.append(post); + + return posts_by_id @return_error_info @nested_query_compat @@ -258,6 +327,18 @@ async def get_discussions_by_feed(context, tag: str = None, start_author: str = valid_limit(limit, 100)) return await load_posts_reblogs(context['db'], res, truncate_body=truncate_body) + #valid_account(start_author, allow_empty=True) + #valid_account(tag) + #valid_permlink(start_permlink, allow_empty=True) + #valid_limit(limit, 100) + + #sql = """ + # + #""" + + #if start_permlink and start_author: + + @return_error_info @nested_query_compat @@ -267,12 +348,45 @@ async def get_discussions_by_comments(context, start_author: str = None, start_p """Get comments by made by author.""" assert start_author, '`start_author` cannot be blank' assert not filter_tags, 'filter_tags not supported' - ids = await cursor.pids_by_account_comments( - context['db'], - valid_account(start_author), - valid_permlink(start_permlink, allow_empty=True), - valid_limit(limit, 100)) - return await load_posts(context['db'], ids, truncate_body=truncate_body) + valid_account(start_author) + valid_permlink(start_permlink, allow_empty=True) + valid_limit(limit, 100) + + sql = """ + ---get_discussions_by_comments + SELECT hive_posts_cache.post_id, hive_posts_cache.author, hive_posts_cache.permlink, + hive_posts_cache.title, hive_posts_cache.body, hive_posts_cache.category, hive_posts_cache.depth, + hive_posts_cache.promoted, hive_posts_cache.payout, hive_posts_cache.payout_at, + hive_posts_cache.is_paidout, hive_posts_cache.children, hive_posts_cache.votes, + hive_posts_cache.created_at, hive_posts_cache.updated_at, hive_posts_cache.rshares, + hive_posts_cache.raw_json, hive_posts_cache.json, hive_accounts.reputation AS author_rep + FROM hive_posts_cache JOIN hive_accounts ON (hive_posts_cache.author = hive_accounts.name) + JOIN hive_posts ON (hive_posts_cache.post_id = hive_posts.id) + WHERE hive_posts_cache.author = :start_author AND hive_posts_cache.depth > 0 + AND NOT hive_posts.is_deleted + """ + + if start_permlink: + sql += """ + AND hive_posts_cache.post_id <= (SELECT hive_posts_cache.post_id FROM + hive_posts_cache WHERE permlink = :start_permlink AND author=:start_author) + """ + + sql += """ + ORDER BY hive_posts_cache.post_id DESC, depth LIMIT :limit + """ + + posts = [] + db = context['db'] + result = await db.query_all(sql, start_author=start_author, start_permlink=start_permlink, limit=limit) + + for row in result: + row = dict(row) + post = _condenser_post_object(row, truncate_body=truncate_body) + post['active_votes'] = _mute_votes(post['active_votes'], Mutes.all()) + posts.append(post) + + return posts @return_error_info @@ -281,6 +395,22 @@ async def get_replies_by_last_update(context, start_author: str = None, start_pe limit: int = 20, truncate_body: int = 0): """Get all replies made to any of author's posts.""" assert start_author, '`start_author` cannot be blank' + #valid_account(start_author) + #valid_permlink(start_permlink, allow_empty=True) + #valid_limit(limit, 100) + + #sql = """ + # SELECT hive_posts_cache.post_id, hive_posts_cache.author, hive_posts_cache.permlink, + # hive_posts_cache.title, hive_posts_cache.body, hive_posts_cache.category, hive_posts_cache.depth, + # hive_posts_cache.promoted, hive_posts_cache.payout, hive_posts_cache.payout_at, + # hive_posts_cache.is_paidout, hive_posts_cache.children, hive_posts_cache.votes, + # hive_posts_cache.created_at, hive_posts_cache.updated_at, hive_posts_cache.rshares, + # hive_posts_cache.raw_json, hive_posts_cache.json, hive_accounts.reputation AS author_rep + # FROM hive_posts_cache JOIN hive_accounts ON (hive_posts_cache.author = hive_accounts.name) + # JOIN hive_posts ON (hive_posts_cache.post_id = hive_posts.id) + # + #""" + ids = await cursor.pids_by_replies_to_account( context['db'], valid_account(start_author), @@ -315,14 +445,9 @@ async def get_post_discussions_by_payout(context, start_author: str = '', start_ limit: int = 20, tag: str = None, truncate_body: int = 0): """Query top-level posts, sorted by payout.""" - ids = await cursor.pids_by_query( - context['db'], - 'payout', - valid_account(start_author, allow_empty=True), - valid_permlink(start_permlink, allow_empty=True), - valid_limit(limit, 100), - valid_tag(tag, allow_empty=True)) - return await load_posts(context['db'], ids, truncate_body=truncate_body) + results = await get_discussions_by('payout', context, start_author, start_permlink, + limit, tag, truncate_body) + return results @return_error_info @@ -332,14 +457,9 @@ async def get_comment_discussions_by_payout(context, start_author: str = '', sta truncate_body: int = 0): """Query comments, sorted by payout.""" # pylint: disable=invalid-name - ids = await cursor.pids_by_query( - context['db'], - 'payout_comments', - valid_account(start_author, allow_empty=True), - valid_permlink(start_permlink, allow_empty=True), - valid_limit(limit, 100), - valid_tag(tag, allow_empty=True)) - return await load_posts(context['db'], ids, truncate_body=truncate_body) + results = await get_discussions_by('payout_comments', context, start_author, start_permlink, + limit, tag, truncate_body) + return results @return_error_info diff --git a/hive/server/db.py b/hive/server/db.py index e7608b4b6b12fb96ec02eeeeff4b67f81ebfd4f3..d6d81e73f22c8de8b51f5cea3823ad3bab5d0a35 100644 --- a/hive/server/db.py +++ b/hive/server/db.py @@ -10,6 +10,7 @@ from aiopg.sa import create_engine from hive.utils.stats import Stats logging.getLogger('sqlalchemy.engine').setLevel(logging.WARNING) +logging.FileHandler('hive_database_timer.log') log = logging.getLogger(__name__) def sqltimer(function): diff --git a/hive/utils/account.py b/hive/utils/account.py index caf38307c378d39473ae3d997d762462679bc768..6502f5aa61ba1b427b630b621da17ad50182a420 100644 --- a/hive/utils/account.py +++ b/hive/utils/account.py @@ -41,6 +41,8 @@ def safe_profile_metadata(account): website = None if website and not _valid_url_proto(website): website = 'http://' + website + if website and len(website) > 100: + website = None if profile_image and not _valid_url_proto(profile_image): profile_image = None diff --git a/hive/utils/stats.py b/hive/utils/stats.py index fc7d3a5543196e5d08658efeb25726a3b5a14a8a..e5705007496df317c21c36c79860ce854707e41c 100644 --- a/hive/utils/stats.py +++ b/hive/utils/stats.py @@ -6,7 +6,10 @@ import logging from time import perf_counter as perf from hive.utils.system import colorize, peak_usage_mb +file_handler = logging.FileHandler('database_timer.log') +file_handler.setLevel(logging.INFO) log = logging.getLogger(__name__) +log.addHandler(file_handler) def _normalize_sql(sql, maxlen=180): """Collapse whitespace and middle-truncate if needed.""" @@ -116,6 +119,7 @@ class DbStats(StatsAbstract): def check_timing(self, call, ms, batch_size): """Warn if any query is slower than defined threshold.""" + log.warning("[SQL][%dms] %s", ms, call) if ms > self.SLOW_QUERY_MS: out = "[SQL][%dms] %s" % (ms, call[:250]) log.warning(colorize(out)) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..b7dd598b2b2833b437cf02861d2dc32202a6026d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,79 @@ +aiocache==0.11.1 +aiohttp==3.6.2 +aiopg==1.0.0 +apply-defaults==0.1.4 +asn1crypto==0.24.0 +async-timeout==3.0.1 +attrs==19.3.0 +Automat==0.6.0 +certifi==2020.4.5.1 +chardet==3.0.4 +click==6.7 +colorama==0.3.7 +command-not-found==0.3 +ConfigArgParse==1.2 +configobj==5.0.6 +constantly==15.1.0 +cryptography==2.1.4 +dateparser==0.7.4 +distro-info===0.18ubuntu0.18.04.1 +funcsigs==1.0.2 +funcy==1.14 +-e git+git@gitlab.syncad.com:blocktrades/hivemind.git@ca1cccc0dbc4d78fdfe009afc251f7ee760b6847#egg=hivemind +httplib2==0.9.2 +humanize==2.3.0 +hyperlink==17.3.1 +idna==2.9 +idna-ssl==1.1.0 +incremental==16.10.1 +iotop==0.6 +Jinja2==2.10 +jsonrpcserver==4.0.1 +jsonschema==2.6.0 +keyring==10.6.0 +keyrings.alt==3.0 +language-selector==0.1 +Mako==1.1.2 +Markdown==2.4.1 +MarkupSafe==1.1.1 +maya==0.6.1 +multidict==4.7.5 +netifaces==0.10.4 +PAM==0.4.2 +pdoc==0.3.2 +pendulum==2.1.0 +psycopg2-binary==2.8.5 +pyasn1==0.4.2 +pyasn1-modules==0.2.1 +pycrypto==2.6.1 +pygobject==3.26.1 +pyOpenSSL==17.5.0 +pyserial==3.4 +python-apt==1.6.5+ubuntu0.2 +python-dateutil==2.8.1 +python-debian==0.1.32 +pytz==2019.3 +pytzdata==2019.3 +pyxdg==0.25 +PyYAML==3.12 +regex==2020.4.4 +requests==2.18.4 +requests-unixsocket==0.1.5 +SecretStorage==2.3.1 +service-identity==16.0.0 +six==1.14.0 +snaptime==0.2.4 +SQLAlchemy==1.3.15 +ssh-import-id==5.7 +systemd-python==234 +toolz==0.10.0 +Twisted==17.9.0 +typing-extensions==3.7.4.2 +tzlocal==2.0.0 +ufw==0.36 +ujson==2.0.3 +unattended-upgrades==0.1 +urllib3==1.25.8 +virtualenv==15.1.0 +yarl==1.4.2 +zope.interface==4.3.2