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

Use delete_files from core when deleting a Realm. #2401

Merged
merged 36 commits into from
Jun 29, 2021
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
5fd821f
Use delete_files from core when deleting a Realm.
DominicFrei May 27, 2021
84be8d0
Remove assert that's caught by Core already.
DominicFrei May 27, 2021
b9ff5df
Update to newest v11 commit.
DominicFrei Jun 4, 2021
c20b263
Make sure we don't accumulate test resources endlessly (#2423)
nirinchev Jun 8, 2021
5e4dab9
Merge remote-tracking branch 'origin/master' into df/use-delete-realm…
DominicFrei Jun 8, 2021
3dc9251
Test calling DeleteRealm multiple times.
DominicFrei Jun 8, 2021
8b93783
Throw a more specific RealmInUseException.
DominicFrei Jun 8, 2021
790ea5d
Merge remote-tracking branch 'origin/master' into df/use-delete-realm…
DominicFrei Jun 9, 2021
321d250
Clarify summary.
DominicFrei Jun 9, 2021
44de1d6
Update function description with new exception.
DominicFrei Jun 9, 2021
3f9471e
Merge remote-tracking branch 'origin/master' into df/use-delete-realm…
DominicFrei Jun 10, 2021
ae34a1c
Merge remote-tracking branch 'origin/master' into df/use-delete-realm…
DominicFrei Jun 10, 2021
0da4467
Merge remote-tracking branch 'origin/master' into df/use-delete-realm…
DominicFrei Jun 11, 2021
44a9fde
Add sync test for multiple deletions.
DominicFrei Jun 14, 2021
846340b
Fix Sync timing isuse.
DominicFrei Jun 14, 2021
27fb8d5
Push test reproing the delete exception
nirinchev Jun 14, 2021
c31c00a
Merge branch 'df/use-delete-realm-from-core' of https://github.com/re…
DominicFrei Jun 14, 2021
237052b
Check if folder still exists.
DominicFrei Jun 15, 2021
b8b8970
Fix a race condition and add some comment for it.
DominicFrei Jun 15, 2021
41f7899
Merge remote-tracking branch 'origin/master' into df/use-delete-realm…
DominicFrei Jun 15, 2021
6e948e2
Increase wait.
DominicFrei Jun 15, 2021
79a5921
Update Realm/Realm/Exceptions/RealmInUseException.cs
Jun 15, 2021
a7c2cf4
Implement PR changes.
DominicFrei Jun 15, 2021
2c9926e
Merge branch 'df/use-delete-realm-from-core' of https://github.com/re…
DominicFrei Jun 15, 2021
16bad83
Re-add delay to avoid code bug.
DominicFrei Jun 16, 2021
6002391
Move delay to the test.
DominicFrei Jun 16, 2021
d16e023
Update Tests/Realm.Tests/Sync/SyncTestBase.cs
Jun 16, 2021
ee63760
Update Tests/Realm.Tests/Sync/SynchronizedInstanceTests.cs
Jun 16, 2021
198e9a2
Test deactivate wait.
DominicFrei Jun 21, 2021
c8b17a4
Remove solved wait.
DominicFrei Jun 21, 2021
20fc494
Merge remote-tracking branch 'origin/master' into df/use-delete-realm…
DominicFrei Jun 24, 2021
9f7aa30
Remove check if file exists which was moved to Core.
DominicFrei Jun 24, 2021
82bae01
Add reproduction test.
DominicFrei Jun 24, 2021
9cebcbf
Remove delay.
DominicFrei Jun 24, 2021
95ca49d
WIP
DominicFrei Jun 25, 2021
ed8e20e
Update to v11.
DominicFrei Jun 29, 2021
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
3 changes: 3 additions & 0 deletions Realm/Realm/Exceptions/RealmException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ internal static Exception Create(RealmExceptionCodes exceptionCode, string messa

return new SessionException(message, code);

case RealmExceptionCodes.RealmInUseException:
return new RealmInUseException(message);

default:
return new Exception(message);
}
Expand Down
1 change: 1 addition & 0 deletions Realm/Realm/Exceptions/RealmExceptionCodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ internal enum RealmExceptionCodes : sbyte
RealmClosed = 24,
ObjectManagedByAnotherRealm = 25,
KeyAlreadyExists = 26,
RealmInUseException = 27,

RealmDotNetExceptionDuringMigration = 30,

Expand Down
31 changes: 31 additions & 0 deletions Realm/Realm/Exceptions/RealmInUseException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2021 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License")
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

namespace Realms.Exceptions
{
/// <summary>
/// An exception thrown when an an operation is being performed on an open Realm
/// that can only performed on closed Realm (i.e. Realm files).
DominicFrei marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public class RealmInUseException : RealmException
{
internal RealmInUseException(string message) : base(message)
{
}
}
}
11 changes: 11 additions & 0 deletions Realm/Realm/Handles/SharedRealmHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ public static extern IntPtr open_with_sync_async(Configuration configuration, Sy
[DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_close_realm", CallingConvention = CallingConvention.Cdecl)]
public static extern void close_realm(SharedRealmHandle sharedRealm, out NativeException ex);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_delete_files", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern void delete_files([MarshalAs(UnmanagedType.LPWStr)] string path,
IntPtr path_len,
out NativeException ex);
DominicFrei marked this conversation as resolved.
Show resolved Hide resolved

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_close_all_realms", CallingConvention = CallingConvention.Cdecl)]
public static extern void close_all_realms(out NativeException ex);

Expand Down Expand Up @@ -263,6 +268,12 @@ public void CloseRealm()
nativeException.ThrowIfNecessary();
}

public static void DeleteFiles(string path)
{
NativeMethods.delete_files(path, (IntPtr)path.Length, out var nativeException);
nativeException.ThrowIfNecessary();
}

public static void CloseAllRealms()
{
NativeMethods.close_all_realms(out var nativeException);
Expand Down
26 changes: 7 additions & 19 deletions Realm/Realm/Realm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,30 +171,18 @@ public static bool Compact(RealmConfigurationBase config = null)
/// <summary>
/// Deletes all the files associated with a realm.
/// </summary>
/// <remarks>
/// The realm file must not be open on other threads.
/// This method must not be called inside a transaction.
DominicFrei marked this conversation as resolved.
Show resolved Hide resolved
/// All but the .lock file will be deleted by this.
/// </remarks>
/// <param name="configuration">A <see cref="RealmConfigurationBase"/> which supplies the realm path.</param>
/// <exception cref="RealmPermissionDeniedException">Thrown if the Realm is still open.</exception>
public static void DeleteRealm(RealmConfigurationBase configuration)
{
Argument.NotNull(configuration, nameof(configuration));

var fullpath = configuration.DatabasePath;
if (IsRealmOpen(fullpath))
{
throw new RealmPermissionDeniedException("Unable to delete Realm because it is still open.");
}

var filesToDelete = new[] { string.Empty, ".log_a", ".log_b", ".log", ".lock", ".note" }
.Select(ext => fullpath + ext)
.Where(File.Exists);

foreach (var file in filesToDelete)
{
File.Delete(file);
}

if (Directory.Exists($"{fullpath}.management"))
{
Directory.Delete($"{fullpath}.management", recursive: true);
}
SharedRealmHandle.DeleteFiles(configuration.DatabasePath);
}

private static bool IsRealmOpen(string path)
Expand Down
19 changes: 18 additions & 1 deletion Tests/Realm.Tests/Database/InstanceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,23 @@ public void DeleteRealmWorksIfClosed()
Assert.That(File.Exists(config.DatabasePath), Is.False);
}

[Test]
public void DeleteRealmWorksIfCalledMultipleTimes()
{
// Arrange
var config = RealmConfiguration.DefaultConfiguration;
var openRealm = GetRealm(config);

// Act
openRealm.Dispose();

// Assert
Assert.That(File.Exists(config.DatabasePath));
Assert.DoesNotThrow(() => Realm.DeleteRealm(config));
Assert.That(File.Exists(config.DatabasePath), Is.False);
Assert.DoesNotThrow(() => Realm.DeleteRealm(config));
}

[Test]
public void GetUniqueInstancesDifferentThreads()
{
Expand Down Expand Up @@ -141,7 +158,7 @@ public void DeleteRealmFailsIfOpenSameThread()
using var openRealm = GetRealm();

// Assert
Assert.Throws<RealmPermissionDeniedException>(() => Realm.DeleteRealm(RealmConfiguration.DefaultConfiguration));
Assert.Throws<RealmInUseException>(() => Realm.DeleteRealm(RealmConfiguration.DefaultConfiguration));
}

[Test]
Expand Down
8 changes: 4 additions & 4 deletions Tests/Realm.Tests/RealmTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace Realms.Tests
[Preserve(AllMembers = true)]
public abstract class RealmTest
{
private readonly List<Realm> _realms = new List<Realm>();
private readonly Queue<Realm> _realms = new Queue<Realm>();
private Logger _originalLogger;
private LogLevel _originalLogLevel;

Expand Down Expand Up @@ -69,7 +69,7 @@ protected virtual void CustomSetUp()

protected void CleanupOnTearDown(Realm realm)
{
_realms.Add(realm);
_realms.Enqueue(realm);
}

[TearDown]
Expand Down Expand Up @@ -100,12 +100,12 @@ protected virtual void CustomTearDown()
realm.Dispose();
}

foreach (var realm in _realms)
_realms.DrainQueue(realm =>
{
// TODO: this should be an assertion but fails on our migration tests due to https://github.com/realm/realm-core/issues/4605.
// Assert.That(DeleteRealmWithRetries(realm), Is.True, "Couldn't delete a Realm on teardown.");
DeleteRealmWithRetries(realm);
}
});
}

private static bool DeleteRealmWithRetries(Realm realm)
Expand Down
11 changes: 3 additions & 8 deletions Tests/Realm.Tests/Sync/FunctionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace Realms.Tests.Sync
[TestFixture, Preserve(AllMembers = true)]
public class FunctionsTests : SyncTestBase
{
private readonly List<string> _conventionsToRemove = new List<string>();
private readonly Queue<string> _conventionsToRemove = new Queue<string>();

[Test]
public void CallFunction_ReturnsResult()
Expand Down Expand Up @@ -490,12 +490,7 @@ protected override void CustomTearDown()
{
base.CustomTearDown();

foreach (var convention in _conventionsToRemove)
{
ConventionRegistry.Remove(convention);
}

_conventionsToRemove.Clear();
_conventionsToRemove.DrainQueue(ConventionRegistry.Remove);
}

private static void AssertDateTimeEquals(DateTime first, DateTime second)
Expand All @@ -518,7 +513,7 @@ private void AddCamelCaseConvention()
pack.Add(new CamelCaseElementNameConvention());
ConventionRegistry.Register(name, pack, _ => true);

_conventionsToRemove.Add(name);
_conventionsToRemove.Enqueue(name);
}

private class FunctionArgument
Expand Down
18 changes: 6 additions & 12 deletions Tests/Realm.Tests/Sync/SyncTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ namespace Realms.Tests.Sync
[Preserve(AllMembers = true)]
public abstract class SyncTestBase : RealmTest
{
private readonly List<Session> _sessions = new List<Session>();
private readonly List<App> _apps = new List<App>();
private readonly Queue<Session> _sessions = new Queue<Session>();
private readonly Queue<App> _apps = new Queue<App>();

private App _defaultApp;

Expand All @@ -45,7 +45,7 @@ protected App CreateApp(AppConfiguration config = null)
config ??= SyncTestHelpers.GetAppConfig();

var app = App.Create(config);
_apps.Add(app);
_apps.Enqueue(app);

if (_defaultApp == null)
{
Expand All @@ -57,24 +57,18 @@ protected App CreateApp(AppConfiguration config = null)

protected override void CustomTearDown()
{
foreach (var session in _sessions)
{
session?.CloseHandle();
}
_sessions.DrainQueue(session => session?.CloseHandle());

base.CustomTearDown();

foreach (var app in _apps)
{
app.Handle.ResetForTesting();
}
_apps.DrainQueue(app => app.Handle.ResetForTesting());

_defaultApp = null;
}

protected void CleanupOnTearDown(Session session)
{
_sessions.Add(session);
_sessions.Enqueue(session);
}

protected Session GetSession(Realm realm)
Expand Down
9 changes: 9 additions & 0 deletions Tests/Realm.Tests/TestHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#if NETCOREAPP || NETFRAMEWORK
using System.Runtime.InteropServices;
#endif
using System.Collections.Generic;
using System.Threading.Tasks;
using MongoDB.Bson;
using Nito.AsyncEx;
Expand Down Expand Up @@ -377,6 +378,14 @@ public static string ByteArrayToTestDescription<T>(T arr)
return $"<{byteArr[0]}>";
}

public static void DrainQueue<T>(this Queue<T> queue, Action<T> action)
{
while (queue.Count > 0)
{
action(queue.Dequeue());
}
}

public static IDisposable Subscribe<T>(this IObservable<T> observable, Action<T> onNext)
{
var observer = new FunctionObserver<T>(onNext);
Expand Down
2 changes: 1 addition & 1 deletion wrappers/realm-core
Submodule realm-core updated 169 files
3 changes: 3 additions & 0 deletions wrappers/src/error_handling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ namespace realm {

return { RealmErrorType::AppUnknownError, e.message };
}
catch (const DeleteOnOpenRealmException& e) {
return { RealmErrorType::RealmInUseException, e.what() };
}
catch (const std::bad_alloc& e) {
return { RealmErrorType::RealmOutOfMemory, e.what() };
}
Expand Down
2 changes: 2 additions & 0 deletions wrappers/src/realm_error_type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ namespace realm {
ObjectManagedByAnotherRealm = 25,

KeyAlreadyExists = 26,

RealmInUseException = 27,

RealmDotNetExceptionDuringMigration = 30,

Expand Down
8 changes: 8 additions & 0 deletions wrappers/src/shared_realm_cs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,14 @@ REALM_EXPORT void shared_realm_close_realm(SharedRealm& realm, NativeException::
});
}

REALM_EXPORT void shared_realm_delete_files(uint16_t* path_buf, size_t path_len, NativeException::Marshallable& ex)
{
handle_errors(ex, [&]() {
Utf16StringAccessor path_string(path_buf, path_len);
Realm::delete_files(path_string);
});
}

REALM_EXPORT void shared_realm_close_all_realms(NativeException::Marshallable& ex)
{
s_can_call_managed = false;
Expand Down
2 changes: 1 addition & 1 deletion wrappers/src/sync_user_cs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ extern "C" {
REALM_EXPORT SharedApp* realm_syncuser_get_app(SharedSyncUser& user, NativeException::Marshallable& ex)
{
return handle_errors(ex, [&] {
if (auto shared_app = user->sync_manager()->app().lock()) {
if (auto shared_app = user->sync_manager().app().lock()) {
return new SharedApp(shared_app);
}

Expand Down