Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync schema migrations #7239

Merged
merged 14 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

### Breaking changes
* `App::get_uncached_app(...)` and `App::get_shared_app(...)` have been replaced by `App::get_app(App::CacheMode, ...)`. The App constructor is now enforced to be unusable, use `App::get_app()` instead. ([#7237](https://github.com/realm/realm-core/issues/7237))
* The schema version field in the Realm config had no use for the flexible sync Realms previously. It is now being used for the upcoming Sync Schema Migrations feature. If it was set to a value other than zero, the application will start receiving an error from the server. Data synchronization will be stopped until the Realm is opened with schema version zero. (PR [#7239](https://github.com/realm/realm-core/pull/7239))

### Compatibility
* Fileformat: Generates files with format v23. Reads and automatically upgrade from fileformat v5.
Expand All @@ -18,6 +19,10 @@

### Internals
* Add support for chunked transfer encoding when using `HTTPParser`.
* Bump the sync protocol to v11. The new protocol version comes with the following changes:
- JSON_ERROR server message contains the previous schema version
- Flexible sync BIND client message contains the current schema version
* Add BAAS admin API to create new schema versions (drafts can be used to deploy all changes at once)

----------------------------------------------

Expand Down
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ let notSyncServerSources: [String] = [
"realm/sync/noinst/pending_bootstrap_store.cpp",
"realm/sync/noinst/protocol_codec.cpp",
"realm/sync/noinst/sync_metadata_schema.cpp",
"realm/sync/noinst/sync_schema_migration.cpp",
"realm/sync/object_id.cpp",
"realm/sync/protocol.cpp",
"realm/sync/subscriptions.cpp",
Expand Down
4 changes: 2 additions & 2 deletions dependencies.list
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ VERSION=13.25.1
OPENSSL_VERSION=3.0.8
ZLIB_VERSION=1.2.13
# https://github.com/10gen/baas/commits
# 300ef is 2023 Dec 15
BAAS_VERSION=300efb0604a88f1f36899bee0f42b34826b9b65f
# 5087f is 2024 Jan 13
BAAS_VERSION=5087ffd5a0e4975e625f0fbcceed23107f611055
1 change: 1 addition & 0 deletions src/realm/error_codes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ ErrorCategory ErrorCodes::error_categories(Error code)
case SyncUserMismatch:
case SyncWriteNotAllowed:
case SyncLocalClockBeforeEpoch:
case SyncSchemaMigrationError:
return ErrorCategory().set(ErrorCategory::runtime_error).set(ErrorCategory::sync_error);

case SyncConnectFailed:
Expand Down
3 changes: 3 additions & 0 deletions src/realm/error_codes.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ typedef enum realm_errno {
RLM_ERR_WRONG_SYNC_TYPE = 1043,
RLM_ERR_SYNC_WRITE_NOT_ALLOWED = 1044,
RLM_ERR_SYNC_LOCAL_CLOCK_BEFORE_EPOCH = 1045,
RLM_ERR_SYNC_SCHEMA_MIGRATION_ERROR = 1046,

RLM_ERR_SYSTEM_ERROR = 1999,

Expand Down Expand Up @@ -263,6 +264,8 @@ typedef enum realm_sync_errno_session {
RLM_SYNC_ERR_SESSION_MIGRATE_TO_FLX = 232,
RLM_SYNC_ERR_SESSION_BAD_PROGRESS = 233,
RLM_SYNC_ERR_SESSION_REVERT_TO_PBS = 234,
RLM_SYNC_ERR_SESSION_BAD_SCHEMA_VERSION = 235,
RLM_SYNC_ERR_SESSION_SCHEMA_VERSION_CHANGED = 236,
// Error code 299 is reserved as an "unknown session error" in tests
} realm_sync_errno_session_e;

Expand Down
1 change: 1 addition & 0 deletions src/realm/error_codes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ class ErrorCodes {
WrongSyncType = RLM_ERR_WRONG_SYNC_TYPE,
SyncWriteNotAllowed = RLM_ERR_SYNC_WRITE_NOT_ALLOWED,
SyncLocalClockBeforeEpoch = RLM_ERR_SYNC_LOCAL_CLOCK_BEFORE_EPOCH,
SyncSchemaMigrationError = RLM_ERR_SYNC_SCHEMA_MIGRATION_ERROR,

SystemError = RLM_ERR_SYSTEM_ERROR,

Expand Down
1 change: 1 addition & 0 deletions src/realm/exceptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ InvalidTableRef::~InvalidTableRef() noexcept = default;
SerializationError::~SerializationError() noexcept = default;
NotImplemented::~NotImplemented() noexcept = default;
MigrationFailed::~MigrationFailed() noexcept = default;
SyncSchemaMigrationFailed::~SyncSchemaMigrationFailed() noexcept = default;
ObjectAlreadyExists::~ObjectAlreadyExists() noexcept = default;
CrossTableLinkTarget::~CrossTableLinkTarget() noexcept = default;
SystemError::~SystemError() noexcept = default;
Expand Down
8 changes: 8 additions & 0 deletions src/realm/exceptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,14 @@ struct MigrationFailed : LogicError {
~MigrationFailed() noexcept override;
};

struct SyncSchemaMigrationFailed : LogicError {
SyncSchemaMigrationFailed(std::string_view msg)
: LogicError(ErrorCodes::SyncSchemaMigrationError, msg)
{
}
~SyncSchemaMigrationFailed() noexcept override;
};

struct ObjectAlreadyExists : RuntimeError {
template <class T, class U>
ObjectAlreadyExists(const U& object_type, T pk_val)
Expand Down
9 changes: 5 additions & 4 deletions src/realm/object-store/object_store.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -850,19 +850,20 @@ static void apply_post_migration_changes(Group& group, std::vector<SchemaChange>
void ObjectStore::apply_schema_changes(Transaction& group, uint64_t schema_version, Schema& target_schema,
uint64_t target_schema_version, SchemaMode mode,
std::vector<SchemaChange> const& changes, bool handle_automatically_backlinks,
std::function<void()> migration_function)
std::function<void()> migration_function,
bool set_schema_version_on_version_decrease)
{
create_metadata_tables(group);

if (mode == SchemaMode::AdditiveDiscovered || mode == SchemaMode::AdditiveExplicit) {
bool target_schema_is_newer =
(schema_version < target_schema_version || schema_version == ObjectStore::NotVersioned);
bool set_schema = (schema_version < target_schema_version || schema_version == ObjectStore::NotVersioned ||
set_schema_version_on_version_decrease);
Comment on lines +859 to +860
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check should just be deleted entirely as it's long obsolete. It was originally there because we used to the schema version to decide if we should create and remove indexes.


// With sync v2.x, indexes are no longer synced, so there's no reason to avoid creating them.
bool update_indexes = true;
apply_additive_changes(group, changes, update_indexes);

if (target_schema_is_newer)
if (set_schema)
set_schema_version(group, target_schema_version);

set_schema_keys(group, target_schema);
Expand Down
3 changes: 2 additions & 1 deletion src/realm/object-store/object_store.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ class ObjectStore {
static void apply_schema_changes(Transaction& group, uint64_t schema_version, Schema& target_schema,
uint64_t target_schema_version, SchemaMode mode,
std::vector<SchemaChange> const& changes, bool handle_automatically_backlinks,
std::function<void()> migration_function = {});
std::function<void()> migration_function = {},
bool save_schema_version_on_version_decrease = false);

static void apply_additive_changes(Group&, std::vector<SchemaChange> const&, bool update_indexes);

Expand Down
28 changes: 22 additions & 6 deletions src/realm/object-store/shared_realm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,8 +293,7 @@ bool Realm::schema_change_needs_write_transaction(Schema& schema, std::vector<Sc

switch (m_config.schema_mode) {
case SchemaMode::Automatic:
if (version < m_schema_version && m_schema_version != ObjectStore::NotVersioned)
throw InvalidSchemaVersionException(m_schema_version, version, false);
verify_schema_version_not_decreasing(version);
return true;

case SchemaMode::Immutable:
Expand Down Expand Up @@ -324,8 +323,7 @@ bool Realm::schema_change_needs_write_transaction(Schema& schema, std::vector<Sc
}

case SchemaMode::Manual:
if (version < m_schema_version && m_schema_version != ObjectStore::NotVersioned)
throw InvalidSchemaVersionException(m_schema_version, version, false);
verify_schema_version_not_decreasing(version);
if (version == m_schema_version) {
ObjectStore::verify_no_changes_required(changes);
REALM_UNREACHABLE(); // changes is non-empty so above line always throws
Expand All @@ -335,6 +333,17 @@ bool Realm::schema_change_needs_write_transaction(Schema& schema, std::vector<Sc
REALM_COMPILER_HINT_UNREACHABLE();
}

// Schema version is not allowed to decrease for local and pbs realms.
void Realm::verify_schema_version_not_decreasing(uint64_t version)
{
#if REALM_ENABLE_SYNC
if (m_config.sync_config && m_config.sync_config->flx_sync_requested)
return;
#endif
if (version < m_schema_version && m_schema_version != ObjectStore::NotVersioned)
throw InvalidSchemaVersionException(m_schema_version, version, false);
}

Schema Realm::get_full_schema()
{
if (!m_config.immutable())
Expand Down Expand Up @@ -483,6 +492,12 @@ void Realm::update_schema(Schema schema, uint64_t version, MigrationFunction mig

schema.copy_keys_from(actual_schema, m_config.schema_subset_mode);

bool save_schema_version_on_version_decrease = false;
#if REALM_ENABLE_SYNC
if (m_config.sync_config && m_config.sync_config->flx_sync_requested)
save_schema_version_on_version_decrease = true;
#endif

uint64_t old_schema_version = m_schema_version;
bool additive = m_config.schema_mode == SchemaMode::AdditiveDiscovered ||
m_config.schema_mode == SchemaMode::AdditiveExplicit ||
Expand Down Expand Up @@ -512,11 +527,12 @@ void Realm::update_schema(Schema schema, uint64_t version, MigrationFunction mig

ObjectStore::apply_schema_changes(transaction(), version, m_schema, m_schema_version, m_config.schema_mode,
required_changes, m_config.automatically_handle_backlinks_in_migrations,
wrapper);
wrapper, save_schema_version_on_version_decrease);
}
else {
ObjectStore::apply_schema_changes(transaction(), m_schema_version, schema, version, m_config.schema_mode,
required_changes, m_config.automatically_handle_backlinks_in_migrations);
required_changes, m_config.automatically_handle_backlinks_in_migrations,
nullptr, save_schema_version_on_version_decrease);
REALM_ASSERT_DEBUG(additive ||
(required_changes = ObjectStore::schema_from_group(read_group()).compare(schema)).empty());
}
Expand Down
3 changes: 2 additions & 1 deletion src/realm/object-store/shared_realm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ class Realm : public std::enable_shared_from_this<Realm> {
* will be thrown instead.
*
* If the destination file does not exist, the action performed depends on
* the type of the source and destimation files. If the destination
* the type of the source and destination files. If the destination
* configuration is a non-sync local Realm configuration, a compacted copy
* of the current Transaction's data (which includes uncommitted changes if
* applicable!) is written in streaming form, with no history.
Expand Down Expand Up @@ -556,6 +556,7 @@ class Realm : public std::enable_shared_from_this<Realm> {
void set_schema(Schema const& reference, Schema schema);
bool reset_file(Schema& schema, std::vector<SchemaChange>& changes_required);
bool schema_change_needs_write_transaction(Schema& schema, std::vector<SchemaChange>& changes, uint64_t version);
void verify_schema_version_not_decreasing(uint64_t version);
Schema get_full_schema();

// Ensure that m_schema and m_schema_version match that of the current
Expand Down
Loading