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

Unexpected tipg Service Failure Due to Infinity Value in JSON #204

Open
wasdee opened this issue Feb 5, 2025 · 0 comments
Open

Unexpected tipg Service Failure Due to Infinity Value in JSON #204

wasdee opened this issue Feb 5, 2025 · 0 comments
Assignees

Comments

@wasdee
Copy link

wasdee commented Feb 5, 2025

Today, I found my tipg service fails in an unexpected way. This is rare occurence. I don't expect a fix from this obscure bug.
Just to benefit someone with my workaround.

Error Msg

INFO:     Will watch for changes in these directories: ['/home/ben/Devs/Public/tipg/tipg']
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [64346] using StatReload
['gis']
INFO:     Started server process [64371]
INFO:     Waiting for application startup.
Executing query with parameters: 
schemas=['gis']
tables=None
exclude_tables=None
exclude_table_schemas=None
functions=None
exclude_functions=None
exclude_function_schemas=None
spatial=True
spatial_extent=True
datetime_extent=True
Error executing query: number is infinity when parsed as double: line 1 column 793 (char 792)
ERROR:    Traceback (most recent call last):
  File "/home/ben/Devs/Public/tipg/.venv/lib/python3.12/site-packages/starlette/routing.py", line 693, in lifespan
    async with self.lifespan_context(app) as maybe_state:
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ben/.local/share/mise/installs/python/3.12.7/lib/python3.12/contextlib.py", line 210, in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/home/ben/Devs/Public/tipg/.venv/lib/python3.12/site-packages/fastapi/routing.py", line 133, in merged_lifespan
    async with original_context(app) as maybe_original_state:
               ^^^^^^^^^^^^^^^^^^^^^
  File "/home/ben/.local/share/mise/installs/python/3.12.7/lib/python3.12/contextlib.py", line 210, in __aenter__
    return await anext(self.gen)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/home/ben/Devs/Public/tipg/tipg/main.py", line 45, in lifespan
    await register_collection_catalog(
  File "/home/ben/Devs/Public/tipg/tipg/collections_.py", line 1025, in register_collection_catalog
    app.state.collection_catalog = await get_collection_index(app.state.pool, **kwargs)
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ben/Devs/Public/tipg/tipg/collections_.py", line 938, in get_collection_index
    rows = await conn.fetch_b(
           ^^^^^^^^^^^^^^^^^^^
  File "/home/ben/Devs/Public/tipg/.venv/lib/python3.12/site-packages/buildpg/asyncpg.py", line 64, in fetch_b
    return await self.fetch(query, *args, timeout=_timeout)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ben/Devs/Public/tipg/.venv/lib/python3.12/site-packages/asyncpg/connection.py", line 690, in fetch
    return await self._execute(
           ^^^^^^^^^^^^^^^^^^^^
  File "/home/ben/Devs/Public/tipg/.venv/lib/python3.12/site-packages/asyncpg/connection.py", line 1864, in _execute
    result, _ = await self.__execute(
                ^^^^^^^^^^^^^^^^^^^^^
  File "/home/ben/Devs/Public/tipg/.venv/lib/python3.12/site-packages/asyncpg/connection.py", line 1961, in __execute
    result, stmt = await self._do_execute(
                   ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ben/Devs/Public/tipg/.venv/lib/python3.12/site-packages/asyncpg/connection.py", line 2024, in _do_execute
    result = await executor(stmt, None)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "asyncpg/protocol/protocol.pyx", line 206, in bind_execute
  File "asyncpg/protocol/coreproto.pyx", line 84, in asyncpg.protocol.protocol.CoreProtocol._read_server_messages
  File "asyncpg/protocol/coreproto.pyx", line 239, in asyncpg.protocol.protocol.CoreProtocol._process__bind_execute
  File "asyncpg/protocol/coreproto.pyx", line 532, in asyncpg.protocol.protocol.CoreProtocol._parse_data_msgs
  File "asyncpg/protocol/protocol.pyx", line 840, in asyncpg.protocol.protocol.BaseProtocol._decode_row
  File "asyncpg/protocol/prepared_stmt.pyx", line 328, in asyncpg.protocol.protocol.PreparedStatementState._decode_row
  File "asyncpg/protocol/codecs/base.pyx", line 330, in asyncpg.protocol.protocol.Codec.decode
  File "asyncpg/protocol/codecs/base.pyx", line 327, in asyncpg.protocol.protocol.Codec.decode_in_python
orjson.JSONDecodeError: number is infinity when parsed as double: line 1 column 793 (char 792)

ERROR:    Application startup failed. Exiting.

Investigation

I opened my debugger and tried to dig down what is the root cause of this. since it prev. working on the same schema perfectly.

  1. I tried to bisect and locate a table that might causing this, despite there is no add or change recently.
  2. I duplicate whole schema and try to run new schema against tipg it turn out this is working
  3. I renamed my new schema to the old one, this also still work.

What make this error should be inside the schema. but i have no idea what it is

workaround

CREATE OR REPLACE FUNCTION clone_schema(source_schema text, dest_schema text)
RETURNS void AS $$
DECLARE
    object text;
    buffer text;
    default_ text;
    column_ text;
BEGIN
    EXECUTE 'CREATE SCHEMA ' || dest_schema;

    -- Clone sequences
    FOR object IN
        SELECT sequence_name FROM information_schema.sequences
        WHERE sequence_schema = source_schema
    LOOP
        EXECUTE 'CREATE SEQUENCE ' || quote_ident(dest_schema) || '.' || quote_ident(object);
    END LOOP;

    -- Clone tables, adjust sequence defaults, and copy data
    FOR object IN
        SELECT table_name FROM information_schema.tables
        WHERE table_schema = source_schema
    LOOP
        buffer := quote_ident(dest_schema) || '.' || quote_ident(object);
        EXECUTE 'CREATE TABLE ' || buffer ||
            ' (LIKE ' || quote_ident(source_schema) || '.' || quote_ident(object) ||
            ' INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING DEFAULTS)';

        -- Copy the data
        EXECUTE 'INSERT INTO ' || buffer ||
            ' SELECT * FROM ' || quote_ident(source_schema) || '.' || quote_ident(object);

        -- Update sequence references
        FOR column_, default_ IN
            SELECT column_name, REPLACE(column_default, source_schema, dest_schema)
            FROM information_schema.columns
            WHERE table_schema = dest_schema
              AND table_name = object
              AND column_default LIKE 'nextval(%' || source_schema || '%::regclass)'
        LOOP
            EXECUTE 'ALTER TABLE ' || buffer ||
                ' ALTER COLUMN ' || column_ || ' SET DEFAULT ' || default_;
        END LOOP;
    END LOOP;
END;
$$ LANGUAGE plpgsql VOLATILE;

-- SELECT clone_schema('foo', 'bar');

CREATE OR REPLACE FUNCTION verify_schema_clone(source_schema text, dest_schema text)
RETURNS TABLE (
    table_name text,
    source_count bigint,
    dest_count bigint,
    row_count_match boolean,
    checksum_match boolean
) AS $$
DECLARE
    curr_table text;
    source_count bigint;
    dest_count bigint;
    source_checksum text;
    dest_checksum text;
BEGIN
    FOR curr_table IN
        SELECT t.table_name FROM information_schema.tables t
        WHERE t.table_schema = source_schema
        AND t.table_type = 'BASE TABLE'
    LOOP
        -- Get row counts
        EXECUTE 'SELECT COUNT(*) FROM ' || quote_ident(source_schema) || '.' || quote_ident(curr_table)
            INTO source_count;
        EXECUTE 'SELECT COUNT(*) FROM ' || quote_ident(dest_schema) || '.' || quote_ident(curr_table)
            INTO dest_count;

        -- Calculate checksums for all columns
        EXECUTE 'SELECT MD5(STRING_AGG(CAST(t.* AS TEXT), '''' ORDER BY (SELECT NULL)))
                FROM ' || quote_ident(source_schema) || '.' || quote_ident(curr_table) || ' t'
            INTO source_checksum;
        EXECUTE 'SELECT MD5(STRING_AGG(CAST(t.* AS TEXT), '''' ORDER BY (SELECT NULL)))
                FROM ' || quote_ident(dest_schema) || '.' || quote_ident(curr_table) || ' t'
            INTO dest_checksum;

        table_name := curr_table;
        row_count_match := (source_count = dest_count);
        checksum_match := (source_checksum = dest_checksum);

        RETURN NEXT;
    END LOOP;
END;
$$ LANGUAGE plpgsql;

-- Example usage:
-- SELECT * FROM verify_schema_clone('foo', 'bar');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants