Skip to content

feat: add db writer, types, logging, util and test infrastructure#4

Merged
cendhu merged 9 commits intoLF-Decentralized-Trust-labs:mainfrom
farooqazamwasimnl:feat/02-db-writer
Mar 3, 2026
Merged

feat: add db writer, types, logging, util and test infrastructure#4
cendhu merged 9 commits intoLF-Decentralized-Trust-labs:mainfrom
farooqazamwasimnl:feat/02-db-writer

Conversation

@farooqazamwasimnl
Copy link
Contributor

@farooqazamwasimnl farooqazamwasimnl commented Feb 24, 2026

  • Introduces the database writer layer to persist parsed Hyperledger Fabric blocks into PostgreSQL
  • Adds domain types covering ParsedBlockData, ProcessedBlock, TxRecord, TxNamespaceRecord, ReadOnlyRecord, ReadWriteRecord, BlindWriteRecord, EndorsementRecord, and NamespacePolicyRecord; types are organized hierarchically to eliminate duplicate fields
  • Adds pgtype nullable helpers for converting between Go pointers and PostgreSQL nullable types
  • Adds a pgx connection pool factory
  • Adds BlockWriter that atomically persists a complete block including transactions, namespace entries, reads-only, read-writes, blind-writes, endorsements, and policies within a single DB transaction with rollback on error; all inserts use pgx SendBatch for a single round-trip per table
  • Adds testcontainers-go based test infrastructure supporting both containerised and local PostgreSQL
  • Full test coverage for BlockWriter including rollback on error, blind writes, multiple transactions, nil handling, and policy upserts
  • Adds golangci-lint configuration and lint Makefile target
  • Updates Makefile with test-no-db, test-requires-db, coverage, and clean targets
  • Stacks on top of the db schema PR

Signed-off-by: Farooq Azam Wasim <Farooq.Azm.Wasim-CIC.Netherlands@ibm.com>
Copy link
Contributor

@cendhu cendhu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have done a first pass. Need to do a more in-depth review.

@@ -0,0 +1,165 @@
/*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

directly use flogging from fabric. https://pkg.go.dev/github.com/hyperledger/fabric/common/flogging

this local logging package can be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cendhu Done

TxID string
ValidationCode int32
IsBlindWrite bool
ReadVersion *uint64
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a change of mind on this. I feel it would be more straight-forward and easy to understand read-only entries, read-write entries, blind-write entries rather than this optimization? what do you think? It will reflect the block structure as-is.

Copy link
Contributor Author

@farooqazamwasimnl farooqazamwasimnl Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cendhu I agree this modular approach and I have updated the changed. Done

}

// TxNamespaceRecord represents a namespace within a transaction.
type TxNamespaceRecord struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This just covers one nsID and one nsVersion. The transaction might have touched multiple namespaces. EITHER the method name is wrong or the fields.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cendhu Done

}

// ParsedBlockData contains writes, reads, and namespace records.
type ParsedBlockData struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We always place the caller first and then the callee. Move this method to the top of this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cendhu Done


// ParsedBlockData contains writes, reads, and namespace records.
type ParsedBlockData struct {
Writes []WriteRecord
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this write-records seem to have a lot of duplicates -- if a tx is writing 10 fields, for each field, we would be duplicating

Namespace string
BlockNum uint64
TxNum uint64
TxID string

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cendhu Done


// Namespace constants
const (
MetaNamespaceID = "_meta"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import this from fabric-x-common

Copy link
Contributor Author

@farooqazamwasimnl farooqazamwasimnl Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cendhu Done. I have removed the constant file, the plan is to import such constants from fabric-x-common or fabric-x-committer in future PRs, not define them locally.


// Transaction validation codes
const (
StatusCommitted = 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import this from fabric-x-common

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cendhu Done

)

// API default limits
const (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are these limits used for?

Copy link
Contributor Author

@farooqazamwasimnl farooqazamwasimnl Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cendhu Done. The plan is to import such constants from fabric-x-common or fabric-x-committer in future PRs, not define them locally.

}
}()

q := dbsqlc.New(tx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't you use batch in pgx to apply all changes in a single round-trip to the database? or is it taken care by the dbsqlc?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cendhu Done.. changed to batch processing

@@ -0,0 +1,107 @@
# Stacked PR Plan — fabric-x-block-explorer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cendhu Done

Signed-off-by: Farooq Azam Wasim <Farooq.Azm.Wasim-CIC.Netherlands@ibm.com>
Signed-off-by: Farooq Azam Wasim <Farooq.Azm.Wasim-CIC.Netherlands@ibm.com>
Signed-off-by: Farooq Azam Wasim <Farooq.Azm.Wasim-CIC.Netherlands@ibm.com>
Signed-off-by: Farooq Azam Wasim <Farooq.Azm.Wasim-CIC.Netherlands@ibm.com>
…h pgx

Signed-off-by: Farooq Azam Wasim <Farooq.Azm.Wasim-CIC.Netherlands@ibm.com>
…, tx_blind_writes

Signed-off-by: Farooq Azam Wasim <Farooq.Azm.Wasim-CIC.Netherlands@ibm.com>
Signed-off-by: Farooq Azam Wasim <Farooq.Azm.Wasim-CIC.Netherlands@ibm.com>
BlockInfo.Number already carries the block number. The top-level
Number field on ProcessedBlock was never read and duplicated the
same value set by the parser.

Signed-off-by: Farooq Azam Wasim <Farooq.Azm.Wasim-CIC.Netherlands@ibm.com>
Copy link
Contributor

@cendhu cendhu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

func PrepareTestEnv(t *testing.T) *TestContainer {
t.Helper()

ctx := context.Background()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for test, we should always use t.Context()

}

// cleanDatabase drops all tables for a clean test state.
func cleanDatabase(ctx context.Context, t *testing.T, pool *pgxpool.Pool) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is better to drop the whole database instead of tables.

func TestDatabaseTestEnv(t *testing.T) {
env := NewDatabaseTestEnv(t)

ctx := context.Background()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

t.Context()

@cendhu cendhu merged commit 4609424 into LF-Decentralized-Trust-labs:main Mar 3, 2026
2 checks passed
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

Successfully merging this pull request may close these issues.

2 participants