diff --git a/instructions.txt b/instructions.txt index 0f50d3c..56d59ad 100644 --- a/instructions.txt +++ b/instructions.txt @@ -244,4 +244,44 @@ psql "postgresql://postgres_exporter:postgres@172.16.10.178:5444/postgres" |=============================| |ENDPONT TO DETECT A DEAD LOCK| |=============================| - http://172.16.10.178:9188/metrics | grep -i deadlock \ No newline at end of file + http://172.16.10.178:9188/metrics | grep -i deadlock + + +|========================| +|FEATURES COMPLETED TODAY| +|========================| + +| Requirement | `postgres_exporter` | What you need | +| ----------------------- | ------------------: | --------------------------------- | +| Connections | ✅ | postgres_exporter | +| Transactions | ✅ | postgres_exporter | +| Locks | ✅/⚠️ | PostgreSQL metrics/custom query | +| Blocking sessions | ⚠️ | Custom PostgreSQL query | +| Deadlocks | ✅ | postgres_exporter | +| Long-running queries | ⚠️ | `pg_stat_activity` / custom query | +| WAL | ✅ | postgres_exporter | +| Database health | ✅ | postgres_exporter + Prometheus | +| PostgreSQL CPU usage | ❌ | cAdvisor / process monitoring | +| PostgreSQL memory usage | ❌ | cAdvisor / process monitoring | +| Storage/disk usage | ❌ | node_exporter / cAdvisor | +| Network usage | ❌ | node_exporter / cAdvisor | + + +|==================| +| Whats This for? | +|==================| + +postgres=# SELECT * +FROM spock.replication_set_table; + set_id | set_reloid | set_att_list | set_row_filter +-----------+------------------+--------------+---------------- + 590430464 | ddl_test | | + 590430464 | test_replication | | + 590430464 | lock_test | | +(3 rows) + +postgres=# + + +!!!!!!!!!WHEN TESTING NEW NODES OR REFRESHING THE NODES!!!!!!!!! +SET spock.enable_ddl_replication = off; \ No newline at end of file diff --git a/main.py b/main.py index 5e285af..6fc3f2d 100644 --- a/main.py +++ b/main.py @@ -1,47 +1,229 @@ +from fastapi import FastAPI, HTTPException +from pydantic import BaseModel import psycopg2 -# ===== TEST DATA ===== -# id = 600 -source_node = "node1" -message = "from python with love " - -conn = None - -try: - conn = psycopg2.connect( - host="172.16.10.123", - port=5444, - database="postgres", - user="postgres", - password="postgres" - ) +app = FastAPI() - cursor = conn.cursor() +class RegisterNodeRequest(BaseModel): + node_name: str + host: str + port: int = 5444 + dbname: str = "postgres" + user: str = "postgres" + password: str - cursor.execute(""" - INSERT INTO test_replication (source_node, message) - VALUES (%s, %s) - RETURNING id - """, (source_node, message)) - # Get the ID generated by PostgreSQL - generated_id = cursor.fetchone()[0] +class LinkNodeRequest(BaseModel): + subscription_name: str - conn.commit() + # The node where sub_create() must run + subscriber_host: str + subscriber_port: int = 5444 - print("LOCAL COMMIT: SUCCESS") - print("Generated ID:", generated_id) - print("Source:", source_node) - print("Message:", message) + # The provider node + provider_host: str + provider_port: int = 5444 -except Exception as e: - if conn: - conn.rollback() + dbname: str = "postgres" + user: str = "postgres" + password: str - print("LOCAL COMMIT: FAILED") - print("Error:", e) -finally: - if conn: - conn.close() \ No newline at end of file +#POST /register-node +# { +# "node_name": "node2", +# "host": "172.16.10.110", +# "port": 5444, +# "dbname": "postgres", +# "user": "postgres", +# "password": "postgres" +# } + +# postgres=# SELECT spock.node_add_interface( +# 'node2', +# 'node2_external', +# 'host=172.16.10.110 port=5444 dbname=postgres user=postgres password=postgres' +# ); + + + +@app.post("/register-node") +def register_node(data: RegisterNodeRequest): + + conn = None + cur = None + + try: + # Connect to the node we want to register + conn = psycopg2.connect( + host=data.host, + port=data.port, + dbname=data.dbname, + user=data.user, + password=data.password + ) + + cur = conn.cursor() + + # 1. Create Spock extension + cur.execute(""" + CREATE EXTENSION IF NOT EXISTS spock; + """) + + # 2. Check Spock version + cur.execute(""" + SELECT extname, extversion + FROM pg_extension + WHERE extname = 'spock'; + """) + + spock = cur.fetchone() + + # 3. Build node DSN + dsn = ( + f"host={data.host} " + f"port={data.port} " + f"dbname={data.dbname} " + f"user={data.user} " + f"password={data.password}" + ) + + # 4. Create Spock node + cur.execute(""" + SELECT spock.node_create( + node_name := %s, + dsn := %s + ); + """, ( + data.node_name, + dsn + )) + + node_id = cur.fetchone()[0] + + conn.commit() + + # 5. Show registered nodes + cur.execute(""" + SELECT node_id, node_name + FROM spock.node; + """) + + rows = cur.fetchall() + + nodes = [ + { + "node_id": row[0], + "node_name": row[1] + } + for row in rows + ] + + return { + "status": "success", + "message": "Node registered", + "node_id": node_id, + "spock_version": spock[1], + "nodes": nodes + } + + except Exception as e: + if conn: + conn.rollback() + + raise HTTPException( + status_code=500, + detail=str(e) + ) + + finally: + if cur: + cur.close() + + if conn: + conn.close() + + + +# { +# "subscription_name": "node2_from_node1", +# "subscriber_host": "172.16.10.110", +# "subscriber_port": 5444, +# "provider_host": "172.16.10.123", +# "provider_port": 5444, +# "dbname": "postgres", +# "user": "postgres", +# "password": "postgres" +# } +@app.post("/link-nodes") +def link_nodes(data: LinkNodeRequest): + + conn = None + cur = None + + try: + # IMPORTANT: + # Connect to the subscriber node. + conn = psycopg2.connect( + host=data.subscriber_host, + port=data.subscriber_port, + dbname=data.dbname, + user=data.user, + password=data.password + ) + + cur = conn.cursor() + + # Provider DSN + provider_dsn = ( + f"host={data.provider_host} " + f"port={data.provider_port} " + f"dbname={data.dbname} " + f"user={data.user} " + f"password={data.password}" + ) + + # Create subscription + # Create subscription + cur.execute(""" + SELECT spock.sub_create( + subscription_name := %s, + provider_dsn := %s, + synchronize_structure := true, + synchronize_data := true + ); + """, ( + data.subscription_name, + provider_dsn + )) + + + subscription_id = cur.fetchone()[0] + + conn.commit() + + return { + "status": "success", + "message": "Nodes linked successfully", + "subscription_id": subscription_id, + "subscription_name": data.subscription_name, + "subscriber": data.subscriber_host, + "provider": data.provider_host + } + + except Exception as e: + if conn: + conn.rollback() + + raise HTTPException( + status_code=500, + detail=str(e) + ) + + finally: + if cur: + cur.close() + + if conn: + conn.close() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 85676fd..914f2a2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,16 @@ +annotated-doc==0.0.5 +annotated-types==0.8.0 +anyio==4.14.2 +click==8.5.0 +fastapi==0.141.1 +h11==0.16.0 +idna==3.19 psycopg==3.3.4 psycopg-binary==3.3.4 +psycopg2-binary==2.9.12 +pydantic==2.13.5 +pydantic_core==2.46.5 +starlette==1.6.0 +typing-inspection==0.4.4 typing_extensions==4.16.0 +uvicorn==0.52.4