
You picked a topology - three replicas across three IDCs, or four plus an arbiter, or one of the other shapes laid out in our previous post. Now what? The whiteboard doesn't run anything. Someone has to tell the database where each replica should live, and something has to keep them there when a node dies at 3 AM.
That something is Locality - a single tenant-level attribute that names the target replica layout, and a Root Service that keeps the cluster matching it. Locality is declarative: you don't migrate replicas by hand or script a failover. You declare the distribution, and OceanBase converges to it - then keeps converging after every loss. The rest of this post walks through the vocabulary, the syntax, and the enforcement rules that turn a locality string into something the cluster keeps true.
Locality is written in terms of replica types. OceanBase exposes three replica types in practice - the same three the first post introduced, now in their full grammar.
| Type | Keyword | Clog | Votes in Paxos? | Can be Leader? | Role |
| F | FULL / F | Synchronous | Yes | Yes | Serves writes and strong reads (Leader); weak reads (Follower). Every tenant needs at least one. |
| R | READONLY / R | Asynchronous (Learner) | No | No | Complete data, weak-consistency reads. Read scale-out without inflating quorum. |
| C | COLUMNSTORE / C | Asynchronous (Learner) | No | No | Same consensus role as R, but baseline data stored column-wise. Attaches AP workloads to the same cluster. |
The one line that ties this whole series together: only F replicas vote. R and C receive the clog asynchronously as Learners and serve weak-consistency reads, but they are invisible to the quorum calculation. So when the second post said "two-region 3-IDC keeps a majority alive after a single-IDC failure," the majority it meant is a majority of F replicas. Adding R or C replicas scales reads without changing - and without inflating - the quorum you need to keep writing. That is precisely why they exist.
A locality string is a comma-separated list of TYPE{count}@zone clauses, one per zone:
ALTER TENANT t1 LOCALITY = 'F@z1, F@z2, F@z3, R@z4';Reading it left to right: one full replica in z1, one in z2, one in z3, and one read-only replica in z4. That declares a three-F quorum across three zones plus a read scale-out replica in a fourth - the canonical 3F + R pattern, now spelled out.
A few grammar facts worth knowing:
{N} is per-zone and fixed to 1, so it's almost always omitted - F@z1 is FULL{1}@z1. Three replicas means three clauses (F@z1, F@z2, F@z3), never F{3}@z1.@zone binds the clause to a zone, never to a region. Region is a separate attribute (covered below).A@zone clause. The arbitration service is cluster-level (ALTER SYSTEM ADD ARBITRATION SERVICE …) and enabled per tenant (ENABLE_ARBITRATION_SERVICE). This trips people up: a 2F1A deployment's locality is just F@z1, F@z2 - the arbiter is configured entirely outside locality.One scope note for anyone migrating from V3.x: in OceanBase 4.x, Locality is tenant-level only. Earlier versions allowed Locality at the table, database, and table-group level; that surface area was deliberately removed when 4.x consolidated topology declaration to a single tenant attribute. If your operational scripts still issue ALTER TABLE … LOCALITY = …, they need to move to ALTER TENANT.
Here is the payoff - the seven topologies from the last post, translated into the locality you'd actually write. The surprising part is in the third column.
| Topology | Locality string | What sets the geography | Arbiter? |
| Single IDC 3F | F@z1, F@z2, F@z3 | All zones share one region | - |
| Same-city 3-IDC | F@z1, F@z2, F@z3 | Same region; each zone is a separate IDC | - |
| Two-region 3-IDC (5F, 4+1) | F@z1, F@z2, F@z3, F@z4, F@z5 | z1-z4 in region A, z5 in region B | - |
| Three-region 5-IDC (5F, 2+2+1) | F@z1, F@z2, F@z3, F@z4, F@z5 | z1,z2 in A; z3,z4 in B; z5 in C | - |
| Same-city 2F1A | F@z1, F@z2 | Two IDCs in one region | Configured separately |
| Two-region 4F1A | F@z1, F@z2, F@z3, F@z4 | z1-z4 in primary region; arbiter in secondary | Configured separately |
| Three-region 4F1A | F@z1, F@z2, F@z3, F@z4 | z1,z2 in A; z3,z4 in B; arbiter in C | Configured separately |
Look at rows 1-4. Same-region 3-IDC, two-region 5F, and three-region 5F have nearly identical locality strings - just N copies of F@zone, one entry per zone. The locality says nothing about regions at all. So what makes one of them "same-region" and another "three-region"?
The answer is a separate dimension: the zone-to-region attribute. Region is not part of the locality string - it's a property of each zone, set when the zone is added:
ALTER SYSTEM ADD ZONE zone5 IDC 'sh1', REGION 'shanghai';That zone-to-region assignment - not the locality string - is what places replicas in cities. Locality declares how many F replicas and in which zones; the region attribute declares where those zones physically are. The two are deliberately decoupled, which is why the same F@z1,…,F@z5 string can describe a two-region or a three-region deployment depending only on how the zones map to regions.
Locality isn't a config value that sits in a table. Change it, and the cluster acts.
When you run ALTER TENANT ... LOCALITY = '...', the Root Service diffs the new declaration against the current replica distribution and generates the tasks needed to close the gap: add a replica, drop one, change a replica's type, or move it to a different zone. The statement returns quickly; the data movement happens asynchronously in the background. Declaring locality is signing a new contract - not synchronously hauling data around.
Three rules govern how those changes are sequenced:
oceanbase.DBA_OB_TENANTS.PREVIOUS_LOCALITY holds the old string and a second ALTER TENANT … LOCALITY to a new target is rejected. The exception is a rollback - re-issuing the previous locality string aborts the in-flight change and reverts the tenant. The "add first, then drop" rule is what an IDC retirement looks like in practice:

A locality change is two declarations, not one. To retire a zone, add the new replica first, then drop the old - never replace in place. The transitional 4F state preserves the original 3-of-3 quorum guarantee while data copies to z4.
The contract is also maintained. When a server goes permanently offline, the surviving replicas no longer match the declared locality, so the Root Service automatically rebuilds the missing F replica on another OBServer to restore the declared count and quorum. You don't re-issue the locality; the cluster already knows the target and works to satisfy it. This is the difference between "place these replicas once" and "keep this distribution true" - and it's the whole point of a declarative model. (Watching that movement happen - migration, replication, rebuild - is the next post.)
Locality answers how many, what type, which zone. Two adjacent settings answer the questions it deliberately leaves out - and keeping the boundaries straight avoids most confusion:
primary_zone decides which of the F replicas should host Leaders. Locality pins down the set of voting replicas; primary_zone expresses a preference for where leadership - and therefore the write path and strong reads - should land within that set (using ; for priority tiers and , within a tier, e.g. 'z1;z2,z3'). The docs recommend adjusting primary_zone before a locality change that removes the current primary zone, so leadership moves first and the replica drop is clean.So: locality = replica count and type per zone, region attribute = which region/city, primary_zone = where the Leader sits, unit = which machine and how big. Four dimensions, one tenant.
A locality string is a target state. The interesting question is what the cluster does to reach it - and to get back to it after a node fails. That's the job of migration, replication, and rebuild: the Root Service's machinery for moving replicas and log streams between servers without taking the database offline. The next post follows a locality change from ALTER TENANT to a converged cluster, task by task.
On OceanBase Community Edition, stand up a multi-zone tenant and run:
ALTER TENANT t1 LOCALITY = 'F@z1, F@z2, F@z3, R@z4';Then watch oceanbase.DBA_OB_LS_LOCATIONS as the read-only replica appears in z4 on its own - no manual copy step. Its MEMBER_LIST column shows the three voting F replicas; the LEARNER_LIST column shows the new R replica, a concrete view of "only F votes." Drop the R@z4 clause and watch it get removed. That round trip - target declared, cluster converging - is locality in one minute.
Further reading:

At the OceanBase DevCon 2024, we introduced the OceanBase 4.3.0 Beta, unveiling a brand new columnar engine. This release achieves near petabyte-scale, real-time analytics in seconds, and enhances the integration of TP and AP capabilities.


OpenClaw's memory degrades over time—an architectural limitation, not a configuration issue. seekdb M0 solves this with cloud-based memory that persists across sessions and shares learned experience across agents.


The CIS Benchmark for OceanBase Enterprise Edition V4 is now available — an independently validated security hardening guide referenced by PCI DSS, FedRAMP, and HIPAA.
