graflo.migrate.drift¶
Live schema drift: what a database holds that its declared schema does not.
A database drifts from its schema when something writes to it outside the
declared contract -- a hand-run SET, another loader, a half-applied
migration. Drift is found by introspecting the live database and comparing the
result with the declared :class:~graflo.architecture.schema.Schema.
The comparison is deliberately presence-only: which vertex types, edge types and property names exist on one side and not the other. It does not compare property types, identities or indexes, because introspection cannot report them faithfully -- Cypher backends return untyped properties, identity is guessed from property names, and secondary indexes are not read back. Comparing those would report differences introspection invented, not ones the database has.
Introspection of most backends samples a bounded number of rows per type. A
declared property no sampled row carries is then not seen, which is not proof
that it is absent; :attr:LiveSchemaDrift.sampled says which case applies.
Attributes¶
EdgeKey = tuple[str, str, str]
module-attribute
¶
Classes¶
LiveSchemaDrift
¶
Bases: BaseModel
Presence-only differences between a live database and its schema.
Names are logical where the declared schema maps them, and the raw storage name where it does not (anything undeclared has no logical name).
Source code in graflo/migrate/drift.py
Attributes¶
has_drift
property
¶
True when the database holds anything the schema does not declare.
missing_edges = Field(default_factory=list, description='Declared (source, relation, target) patterns with no edge.')
class-attribute
instance-attribute
¶
missing_properties = Field(default_factory=dict, description='Per declared vertex type present in the database, declared properties no examined node carries.')
class-attribute
instance-attribute
¶
missing_vertices = Field(default_factory=list, description='Declared vertex types with no node in the database (or sample).')
class-attribute
instance-attribute
¶
sampled = Field(default=True, description="True when introspection examined a sample of rows per type, so a missing entry means 'not seen in the sample', not 'absent'.")
class-attribute
instance-attribute
¶
undeclared_edges = Field(default_factory=list, description='(source, relation, target) patterns in the data, not declared.')
class-attribute
instance-attribute
¶
undeclared_properties = Field(default_factory=dict, description='Per declared vertex type, properties present but not declared.')
class-attribute
instance-attribute
¶
undeclared_vertices = Field(default_factory=list, description='Vertex types in the database that the schema does not declare.')
class-attribute
instance-attribute
¶
Functions:¶
compare_live_schema(declared, observed, *, db_flavor, sampled=True)
¶
Compare an introspected schema with the declared one, by presence only.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
declared
|
Schema
|
The schema the database is supposed to follow. |
required |
observed
|
Schema
|
What |
required |
db_flavor
|
DBType
|
The database's flavor, for mapping logical names to storage names. |
required |
sampled
|
bool
|
Whether introspection sampled rows rather than reading a complete catalogue. |
True
|
Returns:
| Name | Type | Description |
|---|---|---|
LiveSchemaDrift |
LiveSchemaDrift
|
every difference, with lists sorted for stable output. |
Source code in graflo/migrate/drift.py
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | |