Skip to content

Commit

Permalink
feat: added cycles verbosity for --restore-in-order
Browse files Browse the repository at this point in the history
fixed the case when condensed graph is not built when subset is not provided
  • Loading branch information
wwoytenko committed Aug 16, 2024
1 parent 72c064b commit 6ef8a7f
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 4 deletions.
6 changes: 6 additions & 0 deletions docs/commands/restore.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,9 @@ tables with foreign key constraints.
tables with cyclic dependencies is to temporarily remove the foreign key constraint (to break the cycle), restore the
data, and then re-add the foreign key constraint once the data restoration is complete.


If your database has cyclic dependencies you will be notified about it but the restoration will continue.

```text
2024-08-16T21:39:50+03:00 WRN cycle between tables is detected: cannot guarantee the order of restoration within cycle cycle=["public.employees","public.departments","public.projects","public.employees"]
```
3 changes: 2 additions & 1 deletion internal/db/postgres/cmd/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,9 +406,10 @@ func (d *Dump) mergeAndWriteToc(ctx context.Context) error {
}

func (d *Dump) writeMetaData(ctx context.Context, startedAt, completedAt time.Time) error {
cycles := d.context.Graph.GetCycledTables()
metadata, err := storageDto.NewMetadata(
d.resultToc, d.tocFileSize, startedAt, completedAt, d.config.Dump.Transformation, d.dumpedObjectSizes,
d.context.DatabaseSchema, d.dumpDependenciesGraph, d.sortedTablesDumpIds,
d.context.DatabaseSchema, d.dumpDependenciesGraph, d.sortedTablesDumpIds, cycles,
)
if err != nil {
return fmt.Errorf("unable build metadata: %w", err)
Expand Down
13 changes: 13 additions & 0 deletions internal/db/postgres/cmd/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -537,12 +537,25 @@ func (r *Restore) prune() {
}
}

func (r *Restore) logWarningsIfHasCycles() {
if len(r.metadata.Cycles) == 0 {
return
}
for _, cycle := range r.metadata.Cycles {
log.Warn().
Strs("cycle", cycle).
Msg("cycle between tables is detected: cannot guarantee the order of restoration within cycle")
}
}

func (r *Restore) sortTocEntriesInTopoOrder() []*toc.Entry {
res := make([]*toc.Entry, 0, len(r.tocObj.Entries))

preDataEnd := 0
postDataStart := 0

r.logWarningsIfHasCycles()

// Find predata last index and postdata first index
for idx, item := range r.tocObj.Entries {
if item.Section == toc.SectionPreData {
Expand Down
3 changes: 3 additions & 0 deletions internal/db/postgres/storage/metadata_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,15 @@ type Metadata struct {
Entries []*Entry `yaml:"entries" json:"entries"`
DependenciesGraph map[int32][]int32 `yaml:"dependencies_graph" json:"dependencies_graph"`
DumpIdsOrder []int32 `yaml:"dump_ids_order" json:"dump_ids_order"`
Cycles [][]string `yaml:"cycles" json:"cycles"`
}

func NewMetadata(
tocObj *toc.Toc, tocFileSize int64, startedAt,
completedAt time.Time, transformers []*domains.Table,
stats map[int32]ObjectSizeStat, databaseSchema []*toolkit.Table,
dependenciesGraph map[int32][]int32, dumpIdsOrder []int32,
cycles [][]string,
) (*Metadata, error) {

var format string
Expand Down Expand Up @@ -168,6 +170,7 @@ func NewMetadata(
DatabaseSchema: databaseSchema,
DependenciesGraph: dependenciesGraph,
DumpIdsOrder: dumpIdsOrder,
Cycles: cycles,
Header: Header{
CreationDate: tocObj.Header.CrtmDateTime.Time(),
DbName: *tocObj.Header.ArchDbName,
Expand Down
29 changes: 27 additions & 2 deletions internal/db/postgres/subset/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,40 @@ func NewGraph(ctx context.Context, tx pgx.Tx, tables []*entries.Table) (*Graph,
edgeIdSequence++
}
}
return &Graph{
g := &Graph{
tables: tables,
graph: graph,
paths: make(map[int]*Path),
edges: edges,
visited: make([]int, len(tables)),
order: make([]int, 0),
reversedGraph: reversedGraph,
}, nil
}
g.buildCondensedGraph()
return g, nil
}

func (g *Graph) GetCycles() [][]*Edge {
var cycles [][]*Edge
for _, c := range g.scc {
if c.hasCycle() {
cycles = append(cycles, c.cycles...)
}
}
return cycles
}

func (g *Graph) GetCycledTables() (res [][]string) {
cycles := g.GetCycles()
for _, c := range cycles {
var tables []string
for _, e := range c {
tables = append(tables, fmt.Sprintf(`%s.%s`, e.from.table.Schema, e.from.table.Name))
}
tables = append(tables, fmt.Sprintf(`%s.%s`, c[len(c)-1].to.table.Schema, c[len(c)-1].to.table.Name))
res = append(res, tables)
}
return res
}

// findSubsetVertexes - finds the subset vertexes in the graph
Expand Down
1 change: 0 additions & 1 deletion internal/db/postgres/subset/set_queries.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package subset

func SetSubsetQueries(graph *Graph) error {
graph.buildCondensedGraph()
graph.findSubsetVertexes()
for _, p := range graph.paths {
if isPathForScc(p, graph) {
Expand Down

0 comments on commit 6ef8a7f

Please sign in to comment.