You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	All: Fixed race condition when a note is being uploaded while it's being modified in the text editor
This commit is contained in:
		| @@ -830,6 +830,8 @@ describe('Synchronizer', function() { | |||||||
| 	})); | 	})); | ||||||
|  |  | ||||||
| 	it('should sync resources', asyncTest(async () => { | 	it('should sync resources', asyncTest(async () => { | ||||||
|  | 		while (insideBeforeEach) await time.msleep(100); | ||||||
|  |  | ||||||
| 		let folder1 = await Folder.save({ title: "folder1" }); | 		let folder1 = await Folder.save({ title: "folder1" }); | ||||||
| 		let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id }); | 		let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id }); | ||||||
| 		await shim.attachFileToNote(note1, __dirname + '/../tests/support/photo.jpg'); | 		await shim.attachFileToNote(note1, __dirname + '/../tests/support/photo.jpg'); | ||||||
|   | |||||||
| @@ -167,7 +167,7 @@ class NoteTextComponent extends React.Component { | |||||||
| 	async componentWillReceiveProps(nextProps) { | 	async componentWillReceiveProps(nextProps) { | ||||||
| 		if ('noteId' in nextProps && nextProps.noteId !== this.props.noteId) { | 		if ('noteId' in nextProps && nextProps.noteId !== this.props.noteId) { | ||||||
| 			await this.reloadNote(nextProps); | 			await this.reloadNote(nextProps); | ||||||
| 			if(this.editor_){ | 			if (this.editor_){ | ||||||
| 				const session = this.editor_.editor.getSession(); | 				const session = this.editor_.editor.getSession(); | ||||||
| 				const undoManager = session.getUndoManager(); | 				const undoManager = session.getUndoManager(); | ||||||
| 				undoManager.reset(); | 				undoManager.reset(); | ||||||
|   | |||||||
| @@ -83,6 +83,7 @@ class DecryptionWorker { | |||||||
| 							}); | 							}); | ||||||
| 							continue; | 							continue; | ||||||
| 						} | 						} | ||||||
|  | 						this.logger().warn('DecryptionWorker: error for: ' + item.id + ' (' + ItemClass.tableName() + ')'); | ||||||
| 						throw error; | 						throw error; | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|   | |||||||
| @@ -320,9 +320,23 @@ class Synchronizer { | |||||||
| 							} | 							} | ||||||
| 						} | 						} | ||||||
|  |  | ||||||
|  | 						// Note: Currently, we set sync_time to update_time, which should work fine given that the resolution is the millisecond. | ||||||
|  | 						// In theory though, this could happen: | ||||||
|  | 						// | ||||||
|  | 						// 1. t0: Editor: Note is modified | ||||||
|  | 						// 2. t0: Sync: Found that note was modified so start uploading it | ||||||
|  | 						// 3. t0: Editor: Note is modified again | ||||||
|  | 						// 4. t1: Sync: Note has finished uploading, set sync_time to t0 | ||||||
|  | 						// | ||||||
|  | 						// Later any attempt to sync will not detect that note was modified in (3) (within the same millisecond as it was being uploaded) | ||||||
|  | 						// because sync_time will be t0 too. | ||||||
|  | 						// | ||||||
|  | 						// The solution would be to use something like an etag (a simple counter incremented on every change) to make sure each | ||||||
|  | 						// change is uniquely identified. Leaving it like this for now. | ||||||
|  |  | ||||||
| 						if (canSync) { | 						if (canSync) { | ||||||
| 							await this.api().setTimestamp(path, local.updated_time); | 							await this.api().setTimestamp(path, local.updated_time); | ||||||
| 							await ItemClass.saveSyncTime(syncTargetId, local, time.unixMs()); | 							await ItemClass.saveSyncTime(syncTargetId, local, local.updated_time); | ||||||
| 						} | 						} | ||||||
|  |  | ||||||
| 					} else if (action == 'itemConflict') { | 					} else if (action == 'itemConflict') { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user