/ * There are two PostgreSQL servers: * PROD - OLTP server, * STATS - server for long analytical queries. Currently, the production database has a large (10Tb) table: CREATE TABLE profiles( id SERIAL, data JSONB ) The table may have "holes", i.e., some `id`s may be skipped. You need to write a program to copy the profiles table from PROD to STATS. The following interfaces are assumed for database interaction: type Row []interface{} type Database interface { // the implementation of the Database interface can re-establish connections // the call to SaveRows is idempotent io.Closer GetMaxID(ctx context.Context) (uint64, error) LoadRows(ctx context.Context, minID, maxID uint64) ([]Row, error) // [minID, maxID] SaveRows(ctx context.Context, rows []Row) error } func Connect(ctx context.Context, dbname string) (Database, error) // CopyTable // If full=false, then continue data transfer from the last error point // If full=true, then transfer all data func CopyTable(fromName string, toName string, full bool) error { // ... your code } If the `full=false` option is passed, the program should continue transferring data from the last error point. If `full=true`, it should transfer all data. **Basic level**: - sequential data transfer in 1 thread - recovery after failures (option `full=false`) Additional information: - if necessary, you can extend the interface by adding your methods - if necessary, you can use the **database/sql** package directly
sobes.tech AI
Answer from AI
To solve the task of copying a large profiles table from the PROD server to STATS with the ability to resume after failures and an option for full copying, the following approach can be implemented:
-
Determine the last successfully copied ID on the STATS server. For this, you can add a method to the Database interface, for example,
GetMaxCopiedID(ctx context.Context) (uint64, error), which will return the maximum id already copied into the table on the STATS server. -
When starting the CopyTable function:
- If full=true, start copying from id=1 (or the minimum id in the PROD table).
- If full=false, request the last copied id on STATS and start copying from id = lastCopiedID + 1.
-
For copying data, use batch ranges of ids, for example, 1000 records at a time. In a loop:
- Get the maximum id in the PROD table via
GetMaxID. - Load rows from PROD with ids in the range [startID, startID+batchSize-1] via
LoadRows. - Save them to STATS via
SaveRows. - Update startID = startID + batchSize.
- Get the maximum id in the PROD table via
-
If a failure occurs during copying, the next run with full=false will resume from the last successfully saved id.
Example of extending the interface and implementing CopyTable:
// Extending the Database interface to get the maximum copied id
func (db Database) GetMaxCopiedID(ctx context.Context) (uint64, error) {
// Implementation depends on the structure of the table on STATS
// For example, SELECT MAX(id) FROM profiles
}
func CopyTable(fromName string, toName string, full bool) error {
ctx := context.Background()
fromDB, err := Connect(ctx, fromName)
if err != nil {
return err
}
defer fromDB.Close()
toDB, err := Connect(ctx, toName)
if err != nil {
return err
}
defer toDB.Close()
var startID uint64 = 1
if !full {
startID, err = toDB.GetMaxCopiedID(ctx)
if err != nil {
return err
}
startID++
}
maxID, err := fromDB.GetMaxID(ctx)
if err != nil {
return err
}
batchSize := uint64(1000)
for currentID := startID; currentID <= maxID; currentID += batchSize {
endID := currentID + batchSize - 1
if endID > maxID {
endID = maxID
}
rows, err := fromDB.LoadRows(ctx, currentID, endID)
if err != nil {
return err
}
if len(rows) == 0 {
continue
}
err = toDB.SaveRows(ctx, rows)
if err != nil {
return err
}
}
return nil
}
Thus, the program sequentially transfers data, recovers after failures, and supports the full copy option.