Summary
The bulk loader silently rewrites a predicate's schema from uid to [uid] when it encounters more than one UID for a subject/predicate pair. This proposal adds a --preserve-schema flag so users can opt into strict behavior: fail the load with a clear error instead of widening the schema.
Current behavior
Given this schema:
and RDF like:
_:abc <somepred> _:123 .
_:abc <somepred> _:456 .
the bulk loader detects the conflict in the reduce phase, prints a warning, and forces the schema to [uid]:
https://github.com/dgraph-io/dgraph/blob/main/dgraph/cmd/bulk/reduce.go#L876-L891
This behavior is deliberate. It came from #3659, which fixed a data-loss bug: before that change, extra UIDs on a scalar uid predicate were silently dropped. Forcing the schema to a list was the conservative choice (keep all the data, let the user sort it out once the cluster is up). The problem is there's no way to opt out. Users who treat their schema file as the source of truth only discover the rewrite after the load completes, and the warning names the predicate but is easy to miss in bulk loader output.
Note this is a bulk-loader-only behavior. The live loader never modifies an existing schema; with <somepred>: uid already defined, live-loaded mutations follow scalar semantics (last write wins).
Proposal
Add a --preserve-schema flag to dgraph bulk (default false, preserving current behavior):
-
--preserve-schema=false (default): current behavior. Warn and widen the predicate to a list.
-
--preserve-schema=true: the schema file is authoritative. When the reducer finds more than one UID for a scalar uid predicate, fail the load with an error naming the predicate and the subject UID, e.g.:
predicate <somepred> is defined as uid (not a list), but subject 0x2 has 2 values;
rerun without --preserve-schema to widen the schema to [uid], or fix the input data
Implementation should be small: the detection already exists in reduce.go, and setSchemaAsList in dgraph/cmd/bulk/schema.go is only called from that one site. The strict path just returns an error instead.
Scope notes
- The check runs in the reduce phase over merged posting lists, so the original RDF line is no longer available. We can report the predicate and subject UID, but per-line attribution would require map-phase tracking across shards. I'd keep that out of scope for the first pass.
- The original request also floated a "keep the scalar schema and last-write-wins" mode. I'd skip that too: which value wins is nondeterministic in a map/reduce load, so it's a footgun. Error-or-widen covers the real use cases.
testBulkSingleUid in systest/bulk_live/common/bulk_live_cases.go locks in the current force-to-list behavior and should gain a strict-mode counterpart.
References
Summary
The bulk loader silently rewrites a predicate's schema from
uidto[uid]when it encounters more than one UID for a subject/predicate pair. This proposal adds a--preserve-schemaflag so users can opt into strict behavior: fail the load with a clear error instead of widening the schema.Current behavior
Given this schema:
and RDF like:
the bulk loader detects the conflict in the reduce phase, prints a warning, and forces the schema to
[uid]:https://github.com/dgraph-io/dgraph/blob/main/dgraph/cmd/bulk/reduce.go#L876-L891
This behavior is deliberate. It came from #3659, which fixed a data-loss bug: before that change, extra UIDs on a scalar
uidpredicate were silently dropped. Forcing the schema to a list was the conservative choice (keep all the data, let the user sort it out once the cluster is up). The problem is there's no way to opt out. Users who treat their schema file as the source of truth only discover the rewrite after the load completes, and the warning names the predicate but is easy to miss in bulk loader output.Note this is a bulk-loader-only behavior. The live loader never modifies an existing schema; with
<somepred>: uidalready defined, live-loaded mutations follow scalar semantics (last write wins).Proposal
Add a
--preserve-schemaflag todgraph bulk(defaultfalse, preserving current behavior):--preserve-schema=false(default): current behavior. Warn and widen the predicate to a list.--preserve-schema=true: the schema file is authoritative. When the reducer finds more than one UID for a scalaruidpredicate, fail the load with an error naming the predicate and the subject UID, e.g.:Implementation should be small: the detection already exists in
reduce.go, andsetSchemaAsListin dgraph/cmd/bulk/schema.go is only called from that one site. The strict path just returns an error instead.Scope notes
testBulkSingleUidin systest/bulk_live/common/bulk_live_cases.go locks in the current force-to-list behavior and should gain a strict-mode counterpart.References