Swift Core Data Query Generation Sqlite error code 769 ( sqlite3_snapshot_open failed ) - swift

I'm using core data query generation,for each read on view context data i do the following
pin context
read (fetch request)
unpin context
Everything is working correctly as expected, but sometimes I get the following error:
error: SQLCore dispatchRequest: exception handling request: , sqlite3_snapshot_open failed with userInfo of {
NSSQLiteErrorDomain = 769;
}
CoreData: error: SQLCore dispatchRequest: exception handling request: , sqlite3_snapshot_open failed with userInfo of {
NSSQLiteErrorDomain = 769;
}
Below the code to pin/unpin the context:
// MARK: Query generation
func pinContext() {
do {
context.reset()
try context.setQueryGenerationFrom(NSQueryGenerationToken.current)
} catch {
fatalError("Context pinning:\(error)")
}
}
func unpinContext() {
do {
try context.setQueryGenerationFrom(nil)
} catch {
fatalError("Context unpinning:\(error)")
}
}
sqlite.org web site for error code (769) SQLITE_ERROR_SNAPSHOT states:
The SQLITE_ERROR_SNAPSHOT result code might be returned when attempting to start a read transaction on an historical version of the database by using the sqlite3_snapshot_open() interface. If the historical snapshot is no longer available, then the read transaction will fail with the SQLITE_ERROR_SNAPSHOT. This error code is only possible if SQLite is compiled with -DSQLITE_ENABLE_SNAPSHOT.
Now, how may I handle or prevent this error in Core data?

bgCTX = container.newBackgroundContext()
bgCTX.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
bgCTX.automaticallyMergesChangesFromParent = true
try bgCTX.setQueryGenerationFrom(NSQueryGenerationToken.current)

Do you have automaticallyMergesChangesFromParent set to true? If so, that seems to cause issues. According to a comment in NSManagedObjectContext:
/* Whether the context automatically merges changes saved to its coordinator or
parent context. Setting this property to YES when the context is pinned to a non-
current query generation is not supported.
*/
#available(iOS 10.0, *)
open var automaticallyMergesChangesFromParent: Bool
If your context is pinned to the "current" query generation token, then it shouldn't be an issue. However, that query generation token could quickly become stale (non-current) if you are saving data to the store on other contexts. As soon as it tries to merge changes from the parent, it gets into an unknown state, which causes this error/crash.
From what I can tell, setting automaticallyMergesChangesFromParent to false when pinning a context is the safest route. Then you can set it to true again when unpinning.
I wish Apple would update their documentation with this, but instead we have to find out about it in a comment in their code.

Related

channelRead not being called in Swift-NIO Datagram's ChannelInboundHandler

I am trying to capture a UDP video stream within a (fresh) vapor application running in Xcode. The data is being streamed by ffmpeg and I can successfully view the stream on the target machine using VLC, which is also the one running the vapor application, using udp://0.0.0.0:5000. I have used various bits of Apple documentation to get to the code below. When I run it, I get these lines of output on the console log, but I wonder if they are not relevant:
2021-07-07 17:59:27.102681+0100 Run[10550:2494617] [si_destination_compare] send failed: Invalid argument
2021-07-07 17:59:27.104056+0100 Run[10550:2494617] [si_destination_compare] send failed: Undefined error: 0
In configure.swift:
try setupClient()
This is the client code:
final class FrameHandler : ChannelInboundHandler {
typealias InboundIn = AddressedEnvelope<ByteBuffer>
typealias OutboundOut = AddressedEnvelope<ByteBuffer>
func channelRead(ctx: ChannelHandlerContext, data: NIOAny) {
// minimal for question
}
func errorCaught(ctx: ChannelHandlerContext, error: Error) {
// minimal for question
}
}
func setupClient() throws {
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let bootstrap = DatagramBootstrap(group: group)
.channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.channelInitializer { channel in
channel.pipeline.addHandler(FrameHandler())
}
defer {
try! group.syncShutdownGracefully()
}
let channel = try bootstrap.bind(host: "0.0.0.0", port: 5000).wait()
try channel.closeFuture.wait()
}
The problem is that although channelRegistered and channelActive are called, followed by a never-ending stream of readComplete, the important one channelRead never gets called - neither does errorCaught. If I comment out the call to setupClient then there is no network activity, however, if it runs then Xcode's network monitor shows activity consistent with the levels in ffmpeg. So, I believe the connection is being set up.
I wonder if the problem is in the way I am setting the handler up? All the examples use echo or reflecting chat examples, so the inbound handler is set up in the closure of the data-writing function using the context rather than adding it in the initialiser (although, the outbound handler is set up in this way).
I'm assuming you're using Vapor 4 which is based on SwiftNIO 2. And in NIO 2, the ChannelHandlerContext variable is called context and not ctx. So if you just rename all your ctx to context, I'd assume it'll work.

Azure Mobile Services for Xamarin Forms - Conflict Resolution

I'm supporting a production Xamarin Forms app with offline sync feature implemented using Azure Mobile Services.
We have a lot of production issues related to users losing data or general instability that goes away if the reinstall the app. After having a look through, I think the issues are around how the conflict resolution is handled in the app.
For every entity that tries to sync we handle MobileServicePushFailedException and then traverse through the errors returned and take action.
catch (MobileServicePushFailedException ex)
{
foreach (var error in ex.PushResult.Errors) // These are MobileServiceTableOpearationErrors
{
var status = error.Status; // HttpStatus code returned
// Take Action based on this status
// If its 409 or 412, we go in to conflict resolving and tries to decide whether the client or server version wins
}
}
The conflict resolving seems too custom to me and I'm checking to see whether there are general guidelines.
For example, we seem to be getting empty values for 'CreatedAt' & 'UpdatedAt' timestamps for local and server versions of the entities returned, which is weird.
var serverItem = error.Result;
var clientItem = error.Item;
// sometimes serverItem.UpdatedAt or clientItem.UpdatedAt is NULL. Since we use these 2 fields to determine who wins, we are stumped here
If anyone can point me to some guideline or sample code on how these conflicts should be generally handled using information from the MobileServiceTableOperationError, that will be highly appreciated
I came across the following code snippet from the following doc.
// Simple error/conflict handling.
if (syncErrors != null)
{
foreach (var error in syncErrors)
{
if (error.OperationKind == MobileServiceTableOperationKind.Update && error.Result != null)
{
//Update failed, reverting to server's copy.
await error.CancelAndUpdateItemAsync(error.Result);
}
else
{
// Discard local change.
await error.CancelAndDiscardItemAsync();
}
Debug.WriteLine(#"Error executing sync operation. Item: {0} ({1}). Operation discarded.",
error.TableName, error.Item["id"]);
}
}
Surfacing conflicts to the UI I found in this doc
private async Task ResolveConflict(TodoItem localItem, TodoItem serverItem)
{
//Ask user to choose the resolution between versions
MessageDialog msgDialog = new MessageDialog(
String.Format("Server Text: \"{0}\" \nLocal Text: \"{1}\"\n",
serverItem.Text, localItem.Text),
"CONFLICT DETECTED - Select a resolution:");
UICommand localBtn = new UICommand("Commit Local Text");
UICommand ServerBtn = new UICommand("Leave Server Text");
msgDialog.Commands.Add(localBtn);
msgDialog.Commands.Add(ServerBtn);
localBtn.Invoked = async (IUICommand command) =>
{
// To resolve the conflict, update the version of the item being committed. Otherwise, you will keep
// catching a MobileServicePreConditionFailedException.
localItem.Version = serverItem.Version;
// Updating recursively here just in case another change happened while the user was making a decision
UpdateToDoItem(localItem);
};
ServerBtn.Invoked = async (IUICommand command) =>
{
RefreshTodoItems();
};
await msgDialog.ShowAsync();
}
I hope this helps provide some direction. Although the Azure Mobile docs have been deprecated, the SDK hasn't changed and should still be relevant. If this doesn't help, let me know what you're using for a backend store.

SignalR Core - Error: Websocket closed with status code: 1006

I use SignalR in an Angular app. When I destroy component in Angular I also want to stop connection to the hub. I use the command:
this.hubConnection.stop();
But I get an error in Chrome console:
Websocket closed with status code: 1006
In Edge: ERROR Error: Uncaught (in promise): Error: Invocation canceled due to connection being closed. Error: Invocation canceled due to connection being closed.
It actually works and connection has been stopped, but I would like to know why I get the error.
This is how I start the hub:
this.hubConnection = new HubConnectionBuilder()
.withUrl("/matchHub")
.build();
this.hubConnection.on("MatchUpdate", (match: Match) => {
// some magic
})
this.hubConnection
.start()
.then(() => {
this.hubConnection.invoke("SendUpdates");
});
EDIT
I finally find the issue. Its caused by change streams from Mongo. If I remove the code from SendUpdates() method then OnDisconnected is triggered.
public class MatchHub : Hub
{
private readonly IMatchManager matchManager;
public MatchHub(IMatchManager matchManager)
{
this.matchManager = matchManager;
}
public async Task SendUpdates() {
using (var changeStream = matchManager.GetChangeStream()) {
while (changeStream.MoveNext()) {
var changeStreamDocument = changeStream.Current.FullDocument;
if (changeStreamDocument == null) {
changeStreamDocument = BsonSerializer.Deserialize<Match>(changeStream.Current.DocumentKey);
}
await Clients.Caller.SendAsync("MatchUpdate", changeStreamDocument);
}
}
}
public override async Task OnDisconnectedAsync(Exception exception)
{
await base.OnDisconnectedAsync(exception);
}
}
Method GetChangeStream from the manager.
ChangeStreamOptions options = new ChangeStreamOptions() { FullDocument = ChangeStreamFullDocumentOption.UpdateLookup };
var watch = mongoDb.Matches.Watch(options).ToEnumerable().GetEnumerator();
return watch;
But I don't know how to fix it.
This can be for many reasons but i think it is most likely this one:
I think this is because of how the server is handling the connected / disconnected events. I can't say for sure but the connection closing needs to handled correctly on the server also with code. Try overriding the built in On Connected /Disconnected methods on the server and see. My assumption only is that you're closing it but the server isn't closing properly and therefore not relaying the proper closed response.
found as a comment at : getting the reason why websockets closed with close code 1006
Where you don't need to change the connection/disconection because evrything works fine. But as an answer this one is the most likely.
It throws error because the callback doesn't get clear properly.
And it is caused by the return data from websocket.
normally it should return like
However, for some reason it might return something like
the very last response breaking into 2 pieces
And that causes the issue.
I don't think there is a way to bypass this without changing the source code.
I reported this on github repo as well at here
It turns out that I can just utilize invocation response to notify client to stop the hub. So it doesn't trigger racing issue.

CloudKit Batch Error: Previous Error in Atomic Zone

I am attempting to save a CKRecord using a CKModifyRecordsOperation and every time I try it, I get this initial error:
["CKErrorDescription": Failed to modify some records,
"CKPartialErrors": {
"CKRecordID: 0x60c000034000; recordName=ABC, zoneID=workspaceZone:DEF" = "CKError 0x60c000257340: \"Batch Request Failed\" (22/2024); \"Record CKRecordID: 0x7fb2f6998a60; recordName=ABC, zoneID=workspaceZone:DEF will not be saved because of previous error in atomic zone\"";
},
"NSDebugDescription": CKInternalErrorDomain: 1011, "NSUnderlyingError": CKError 0x60c000248af0: "Partial Failure" (1011); "Failed to modify some records"; partial errors: {
... 1 "Batch Request Failed" CKError's omited ...
},
"NSLocalizedDescription": Failed to modify some records]
I then parse the individual errors of the batch like this:
if let errorItems = error.partialErrorsByItemID {
for item in errorItems{
if let itemError = item.value as? CKError{
print("::: Individual Error in Batch :::")
print(itemError)
print(":::::")
}
}
}
But all the individual error says is:
CKError(_nsError: CKError 0x60c000257340: "Batch Request Failed" (22/2024); "Record CKRecordID: 0x7fb2f6998a60; recordName=GHI, zoneID=workspaceZone:JKL will not be saved because of previous error in atomic zone")
The CloudKit server log just says it's a BAD_REQUEST which isn't very helpful either.
Is there a way to get more details as to what's wrong with my record?
This just means one of your requests failed. You're doing a batch request with one or more requests. If one fails, CloudKit fails all of the requests to keep things atomic.
So, you should subscribe to errors on each record with perRecordCompletionBlock. Then, you can see which record is failing and why. You should print out the userInfo dictionary of the error for more detailed information.

Swift Core Data: Data not persisting between session. context.save() works for the session only

I am facing a strange issue with CoreData. I am starting a operation to fill initial data in a table. I am starting the operation in applicationDidBecomeActive.
// Creating child context
let context = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
context.parentContext = delegate.managedObjectContext
// Reading data from database and printing here, shows zero number of entities always
context.performBlockAndWait({
// Performing batch delete, to remove duplicacy
})
context.performBlockAndWait({
// Creating entities from the JSON read from App bundle
...
...
do {
// Saving local context
try context.save()
context.parentContext?.performBlockAndWait({
do {
try context.parentContext?.save()
// Reading data from database and printing here, shows correct number of entities
} catch {
printDebug("Unable to save main context: \(error)")
}
})
} catch {
printDebug("Unable to save main context: \(error)")
}
})
// Reading data from database and printing here also, shows correct number of entities
I am starting this operation only from once place i.e applicationDidBecomeActive, and also accessing the entity from this operation only.
Any idea, what is the problem ?
So the problem was batch delete using NSBatchDeleteRequest. I was performing same code for multiple type of NSManagedObjectContext, and all are sub-classes of a single NSManagedObjectContext. So that might be the issue.
When I perform fetch-all-and-delete-in-loop, everything works fine, i.e entities are getting stored. But when I use NSBatchDeleteRequest to delete all at once, the entities that are inserted before the batch-delete operation of the next type of NSManagedObjectContext are getting deleted.
So the culprit was NSBatchDeleteRequest. And I don't know why ? I searched but didn't find any solution. So I will post another question regarding this issue.