diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index d16b73bbc0..08a230112c 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -1757,6 +1757,7 @@ namespace OpenSim.Region.Framework.Scenes { if (group != null) { + group.HasGroupChanged = true; group.ProcessBackup(SimulationDataService, true); } } @@ -2345,13 +2346,12 @@ namespace OpenSim.Region.Framework.Scenes { if (!softDelete) { - // Force a database update so that the scene object group ID is accurate. It's possible that the - // group has recently been delinked from another group but that this change has not been persisted - // to the DB. + // If the group contains prims whose SceneGroupID is incorrect then force a + // database update, because RemoveObject() works by searching on the SceneGroupID. // This is an expensive thing to do so only do it if absolutely necessary. - if (so.HasGroupChangedDueToDelink) - ForceSceneObjectBackup(so); - + if (so.GroupContainsForeignPrims) + ForceSceneObjectBackup(so); + so.DetachFromBackup(); SimulationDataService.RemoveObject(so.UUID, RegionInfo.RegionID); } diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 6f2617602e..a78a0cb027 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -150,12 +150,27 @@ namespace OpenSim.Region.Framework.Scenes get { return m_hasGroupChanged; } } + + private bool m_groupContainsForeignPrims = false; /// - /// Has the group changed due to an unlink operation? We record this in order to optimize deletion, since - /// an unlinked group currently has to be persisted to the database before we can perform an unlink operation. + /// Whether the group contains prims that came from a different group. This happens when + /// linking or delinking groups. The implication is that until the group is persisted, + /// the prims in the database still use the old SceneGroupID. That's a problem if the group + /// is deleted, because we delete groups by searching for prims by their SceneGroupID. /// - public bool HasGroupChangedDueToDelink { get; private set; } + public bool GroupContainsForeignPrims + { + private set + { + m_groupContainsForeignPrims = value; + if (m_groupContainsForeignPrims) + HasGroupChanged = true; + } + + get { return m_groupContainsForeignPrims; } + } + private bool isTimeToPersist() { @@ -1624,7 +1639,7 @@ namespace OpenSim.Region.Framework.Scenes backup_group.RootPart.AngularVelocity = RootPart.AngularVelocity; backup_group.RootPart.ParticleSystem = RootPart.ParticleSystem; HasGroupChanged = false; - HasGroupChangedDueToDelink = false; + GroupContainsForeignPrims = false; m_scene.EventManager.TriggerOnSceneObjectPreSave(backup_group, this); datastore.StoreObject(backup_group, m_scene.RegionInfo.RegionID); @@ -2388,6 +2403,8 @@ namespace OpenSim.Region.Framework.Scenes // If linking prims with different permissions, fix them AdjustChildPrimPermissions(); + GroupContainsForeignPrims = true; + AttachToBackup(); // Here's the deal, this is ABSOLUTELY CRITICAL so the physics scene gets the update about the @@ -2531,9 +2548,16 @@ namespace OpenSim.Region.Framework.Scenes linkPart.Rezzed = RootPart.Rezzed; - // When we delete a group, we currently have to force persist to the database if the object id has changed - // (since delete works by deleting all rows which have a given object id) - objectGroup.HasGroupChangedDueToDelink = true; + // We must persist the delinked group to the database immediately, for safety. The problem + // is that although in memory the new group has a new SceneGroupID, in the database it + // still has the parent group's SceneGroupID (until the next backup). This means that if the + // parent group is deleted then the delinked group will also be deleted from the database. + // This problem will disappear if the region remains alive long enough for another backup, + // since at that time the delinked group's new SceneGroupID will be written to the database. + // But if the region crashes before that then the prims will be permanently gone, and this must + // not happen. (We can't use a just-in-time trick like GroupContainsForeignPrims in this case + // because the delinked group doesn't know when the source group is deleted.) + m_scene.ForceSceneObjectBackup(objectGroup); return objectGroup; } diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectLinkingTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectLinkingTests.cs index 9378e2010d..fa8277cc7b 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectLinkingTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectLinkingTests.cs @@ -143,7 +143,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests Assert.That(grp1.Parts.Length, Is.EqualTo(1), "Group 1 still contained part2 after delink."); Assert.That(part2.AbsolutePosition == Vector3.Zero, "The absolute position should be zero"); - Assert.That(grp3.HasGroupChangedDueToDelink, Is.True); + Assert.That(grp3.GroupContainsForeignPrims, Is.True); } [Test] @@ -349,10 +349,10 @@ namespace OpenSim.Region.Framework.Scenes.Tests // These changes should occur immediately without waiting for a backup pass SceneObjectGroup groupToDelete = sog.DelinkFromGroup(linkPart, false); - - Assert.That(groupToDelete.HasGroupChangedDueToDelink, Is.True); + + Assert.That(groupToDelete.GroupContainsForeignPrims, Is.True); scene.DeleteSceneObject(groupToDelete, false); - Assert.That(groupToDelete.HasGroupChangedDueToDelink, Is.False); + Assert.That(groupToDelete.GroupContainsForeignPrims, Is.False); List storedObjects = scene.SimulationDataService.LoadObjects(scene.RegionInfo.RegionID);